Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Copyright 2003-2010 Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.ehcache.distribution;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Status;
import net.sf.ehcache.event.CacheEventListener;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.ExportException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A cache server which exposes available cache operations remotely through RMI.
*
* It acts as a Decorator to a Cache. It holds an instance of cache, which is a local cache it talks to.
*
* This class could specify a security manager with code like:
*
* Doing so would require the addition of grant statements in the java.policy file.
*
* Per the JDK documentation: "If no security manager is specified no class loading, by RMI clients or servers, is allowed,
* aside from what can be found in the local CLASSPATH." The classpath of each instance of this class should have
* all required classes to enable distribution, so no remote classloading is required or desirable. Accordingly,
* no security manager is set and there are no special JVM configuration requirements.
*
* This class opens a ServerSocket. The dispose method should be called for orderly closure of that socket. This class
* has a shutdown hook which calls dispose() as a convenience feature for developers.
*
* @author Greg Luck
* @version $Id: RMICacheManagerPeerListener.java 3662 2011-02-18 18:27:20Z lorban $
*/
public class RMICacheManagerPeerListener implements CacheManagerPeerListener {
private static final Logger LOG = LoggerFactory.getLogger(RMICacheManagerPeerListener.class.getName());
private static final int MINIMUM_SENSIBLE_TIMEOUT = 200;
private static final int NAMING_UNBIND_RETRY_INTERVAL = 400;
private static final int NAMING_UNBIND_MAX_RETRIES = 10;
/**
* The cache peers. The value is an RMICachePeer.
*/
protected final Map cachePeers = new HashMap();
/**
* status.
*/
protected Status status;
/**
* The RMI listener port
*/
protected Integer port;
private Registry registry;
private boolean registryCreated;
private final String hostName;
private CacheManager cacheManager;
private Integer socketTimeoutMillis;
private Integer remoteObjectPort;
/**
* Constructor with full arguments.
*
* @param hostName may be null, in which case the hostName will be looked up. Machines with multiple
* interfaces should specify this if they do not want it to be the default NIC.
* @param port a port in the range 1025 - 65536
* @param remoteObjectPort the port number on which the remote objects bound in the registry receive calls.
This defaults to a free port if not specified.
* @param cacheManager the CacheManager this listener belongs to
* @param socketTimeoutMillis TCP/IP Socket timeout when waiting on response
*/
public RMICacheManagerPeerListener(String hostName, Integer port, Integer remoteObjectPort, CacheManager cacheManager,
Integer socketTimeoutMillis) throws UnknownHostException {
status = Status.STATUS_UNINITIALISED;
if (hostName != null && hostName.length() != 0) {
this.hostName = hostName;
if (hostName.equals("localhost")) {
LOG.warn("Explicitly setting the listener hostname to 'localhost' is not recommended. "
+ "It will only work if all CacheManager peers are on the same machine.");
}
} else {
this.hostName = calculateHostAddress();
}
if (port == null || port.intValue() == 0) {
assignFreePort(false);
} else {
this.port = port;
}
//by default is 0, which is ok.
this.remoteObjectPort = remoteObjectPort;
this.cacheManager = cacheManager;
if (socketTimeoutMillis == null || socketTimeoutMillis.intValue() < MINIMUM_SENSIBLE_TIMEOUT) {
throw new IllegalArgumentException("socketTimoutMillis must be a reasonable value greater than 200ms");
}
this.socketTimeoutMillis = socketTimeoutMillis;
}
/**
* Assigns a free port to be the listener port.
*
* @throws IllegalStateException if the statis of the listener is not {@link net.sf.ehcache.Status#STATUS_UNINITIALISED}
*/
protected void assignFreePort(boolean forced) throws IllegalStateException {
if (status != Status.STATUS_UNINITIALISED) {
throw new IllegalStateException("Cannot change the port of an already started listener.");
}
this.port = Integer.valueOf(this.getFreePort());
if (forced) {
LOG.warn("Resolving RMI port conflict by automatically using a free TCP/IP port to listen on: " + this.port);
} else {
LOG.debug("Automatically finding a free TCP/IP port to listen on: " + this.port);
}
}
/**
* Calculates the host address as the default NICs IP address
*
* @throws UnknownHostException
*/
protected String calculateHostAddress() throws UnknownHostException {
return InetAddress.getLocalHost().getHostAddress();
}
/**
* Gets a free server socket port.
*
* @return a number in the range 1025 - 65536 that was free at the time this method was executed
* @throws IllegalArgumentException
*/
protected int getFreePort() throws IllegalArgumentException {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(0);
return serverSocket.getLocalPort();
} catch (IOException e) {
throw new IllegalArgumentException("Could not acquire a free port number.");
} finally {
if (serverSocket != null && !serverSocket.isClosed()) {
try {
serverSocket.close();
} catch (Exception e) {
LOG.debug("Error closing ServerSocket: " + e.getMessage());
}
}
}
}
/**
* {@inheritDoc}
*/
public void init() throws CacheException {
if (!status.equals(Status.STATUS_UNINITIALISED)) {
return;
}
RMICachePeer rmiCachePeer = null;
try {
startRegistry();
int counter = 0;
populateListOfRemoteCachePeers();
synchronized (cachePeers) {
for (Iterator iterator = cachePeers.values().iterator(); iterator.hasNext();) {
rmiCachePeer = (RMICachePeer) iterator.next();
bind(rmiCachePeer.getUrl(), rmiCachePeer);
counter++;
}
}
LOG.debug(counter + " RMICachePeers bound in registry for RMI listener");
status = Status.STATUS_ALIVE;
} catch (Exception e) {
String url = null;
if (rmiCachePeer != null) {
url = rmiCachePeer.getUrl();
}
throw new CacheException("Problem starting listener for RMICachePeer "
+ url + ". Initial cause was " + e.getMessage(), e);
}
}
/**
* Bind a cache peer
*
* @param rmiCachePeer
*/
protected void bind(String peerName, RMICachePeer rmiCachePeer) throws Exception {
Naming.rebind(peerName, rmiCachePeer);
}
/**
* Returns a list of bound objects.
*
* This should match the list of cachePeers i.e. they should always be bound
*
* @return a list of String representations of RMICachePeer objects
*/
protected String[] listBoundRMICachePeers() throws CacheException {
try {
return registry.list();
} catch (RemoteException e) {
throw new CacheException("Unable to list cache peers " + e.getMessage());
}
}
/**
* Returns a reference to the remote object.
*
* @param name the name of the cache e.g. sampleCache1
*/
protected Remote lookupPeer(String name) throws CacheException {
try {
return registry.lookup(name);
} catch (Exception e) {
throw new CacheException("Unable to lookup peer for replicated cache " + name + " "
+ e.getMessage());
}
}
/**
* Should be called on init because this is one of the last things that should happen on CacheManager startup.
*/
protected void populateListOfRemoteCachePeers() throws RemoteException {
String[] names = cacheManager.getCacheNames();
for (int i = 0; i < names.length; i++) {
String name = names[i];
Ehcache cache = cacheManager.getEhcache(name);
synchronized (cachePeers) {
if (cachePeers.get(name) == null) {
if (isDistributed(cache)) {
RMICachePeer peer;
if (cache.getCacheConfiguration().getTransactionalMode().isTransactional()) {
peer = new TransactionalRMICachePeer(cache, hostName, port, remoteObjectPort, socketTimeoutMillis);
} else {
peer = new RMICachePeer(cache, hostName, port, remoteObjectPort, socketTimeoutMillis);
}
cachePeers.put(name, peer);
}
}
}
}
}
/**
* Determine if the given cache is distributed.
*
* @param cache the cache to check
* @return true if a CacheReplicator is found in the listeners
*/
protected boolean isDistributed(Ehcache cache) {
Set listeners = cache.getCacheEventNotificationService().getCacheEventListeners();
for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
if (cacheEventListener instanceof CacheReplicator) {
return true;
}
}
return false;
}
/**
* Start the rmiregistry.
*
* The alternative is to use the rmiregistry binary, in which case:
*
*
rmiregistry running
*
-Djava.rmi.server.codebase="file:///Users/gluck/work/ehcache/build/classes/ file:///Users/gluck/work/ehcache/lib/commons-logging-1.0.4.jar"
*
*
* @throws RemoteException
*/
protected void startRegistry() throws RemoteException {
try {
registry = LocateRegistry.getRegistry(port.intValue());
try {
registry.list();
} catch (RemoteException e) {
//may not be created. Let's create it.
registry = LocateRegistry.createRegistry(port.intValue());
registryCreated = true;
}
} catch (ExportException exception) {
LOG.error("Exception starting RMI registry. Error was " + exception.getMessage(), exception);
}
}
/**
* Stop the rmiregistry if it was started by this class.
*
* @throws RemoteException
*/
protected void stopRegistry() throws RemoteException {
if (registryCreated) {
// the unexportObject call must be done on the Registry object returned
// by createRegistry not by getRegistry, a NoSuchObjectException is
// thrown otherwise
boolean success = UnicastRemoteObject.unexportObject(registry, true);
if (success) {
LOG.debug("rmiregistry unexported.");
} else {
LOG.warn("Could not unexport rmiregistry.");
}
}
}
/**
* Stop the listener. It
*
*
unbinds the objects from the registry
*
unexports Remote objects
*
*/
public void dispose() throws CacheException {
if (!status.equals(Status.STATUS_ALIVE)) {
return;
}
try {
int counter = 0;
synchronized (cachePeers) {
for (Iterator iterator = cachePeers.values().iterator(); iterator.hasNext();) {
RMICachePeer rmiCachePeer = (RMICachePeer) iterator.next();
disposeRMICachePeer(rmiCachePeer);
counter++;
}
stopRegistry();
}
LOG.debug(counter + " RMICachePeers unbound from registry in RMI listener");
status = Status.STATUS_SHUTDOWN;
} catch (Exception e) {
throw new CacheException("Problem unbinding remote cache peers. Initial cause was " + e.getMessage(), e);
}
}
/**
* A template method to dispose an individual RMICachePeer. This consists of:
*
*
Unbinding the peer from the naming service
*
Unexporting the peer
*
* Override to specialise behaviour
*
* @param rmiCachePeer the cache peer to dispose of
* @throws Exception thrown if something goes wrong
*/
protected void disposeRMICachePeer(RMICachePeer rmiCachePeer) throws Exception {
unbind(rmiCachePeer);
}
/**
* Unbinds an RMICachePeer and unexports it.
*
* We unbind from the registry first before unexporting.
* Unbinding first removes the very small possibility of a client
* getting the object from the registry while we are trying to unexport it.
*
* This method may take up to 4 seconds to complete, if we are having trouble
* unexporting the peer.
*
* @param rmiCachePeer the bound and exported cache peer
* @throws Exception
*/
protected void unbind(RMICachePeer rmiCachePeer) throws Exception {
String url = rmiCachePeer.getUrl();
try {
Naming.unbind(url);
} catch (NotBoundException e) {
LOG.warn(url + " not bound therefore not unbinding.");
}
// Try to gracefully unexport before forcing it.
boolean unexported = UnicastRemoteObject.unexportObject(rmiCachePeer, false);
for (int count = 1; (count < NAMING_UNBIND_MAX_RETRIES) && !unexported; count++) {
try {
Thread.sleep(NAMING_UNBIND_RETRY_INTERVAL);
} catch (InterruptedException ie) {
// break out of the unexportObject loop
break;
}
unexported = UnicastRemoteObject.unexportObject(rmiCachePeer, false);
}
// If we still haven't been able to unexport, force the unexport
// as a last resort.
if (!unexported) {
if (!UnicastRemoteObject.unexportObject(rmiCachePeer, true)) {
LOG.warn("Unable to unexport rmiCachePeer: " + rmiCachePeer.getUrl() + ". Skipping.");
}
}
}
/**
* All of the caches which are listening for remote changes.
*
* @return a list of RMICachePeer objects. The list if not live
*/
public List getBoundCachePeers() {
List cachePeerList = new ArrayList();
synchronized (cachePeers) {
for (Iterator iterator = cachePeers.values().iterator(); iterator.hasNext();) {
RMICachePeer rmiCachePeer = (RMICachePeer) iterator.next();
cachePeerList.add(rmiCachePeer);
}
}
return cachePeerList;
}
/**
* Returns the listener status.
*/
public Status getStatus() {
return status;
}
/**
* A listener will normally have a resource that only one instance can use at the same time,
* such as a port. This identifier is used to tell if it is unique and will not conflict with an
* existing instance using the resource.
*
* @return a String identifier for the resource
*/
public String getUniqueResourceIdentifier() {
return "RMI listener port: " + port;
}
/**
* If a conflict is detected in unique resource use, this method signals the listener to attempt
* automatic resolution of the resource conflict.
*
* @throws IllegalStateException if the statis of the listener is not {@link net.sf.ehcache.Status#STATUS_UNINITIALISED}
*/
public void attemptResolutionOfUniqueResourceConflict() throws IllegalStateException, CacheException {
assignFreePort(true);
}
/**
* The replication scheme this listener interacts with.
* Each peer provider has a scheme name, which can be used by caches to specify for replication and bootstrap purposes.
*
* @return the well-known scheme name, which is determined by the replication provider author.
*/
public String getScheme() {
return "RMI";
}
/**
* Called immediately after a cache has been added and activated.
*
* Note that the CacheManager calls this method from a synchronized method. Any attempt to call a synchronized
* method on CacheManager from this method will cause a deadlock.
*
* Note that activation will also cause a CacheEventListener status change notification from
* {@link net.sf.ehcache.Status#STATUS_UNINITIALISED} to {@link net.sf.ehcache.Status#STATUS_ALIVE}. Care should be
* taken on processing that notification because:
*
*
the cache will not yet be accessible from the CacheManager.
*
the addCaches methods whih cause this notification are synchronized on the CacheManager. An attempt to call
* {@link net.sf.ehcache.CacheManager#getCache(String)} will cause a deadlock.
*
* The calling method will block until this method returns.
*
* Repopulates the list of cache peers and rebinds the list.
* This method should be called if a cache is dynamically added
*
* @param cacheName the name of the Cache the operation relates to
* @see net.sf.ehcache.event.CacheEventListener
*/
public void notifyCacheAdded(String cacheName) throws CacheException {
LOG.debug("Adding to RMI listener", cacheName);
//Don't add if exists.
synchronized (cachePeers) {
if (cachePeers.get(cacheName) != null) {
return;
}
}
Ehcache cache = cacheManager.getEhcache(cacheName);
if (isDistributed(cache)) {
RMICachePeer rmiCachePeer = null;
String url = null;
try {
if (cache.getCacheConfiguration().getTransactionalMode().isTransactional()) {
rmiCachePeer = new TransactionalRMICachePeer(cache, hostName, port, remoteObjectPort, socketTimeoutMillis);
} else {
rmiCachePeer = new RMICachePeer(cache, hostName, port, remoteObjectPort, socketTimeoutMillis);
}
url = rmiCachePeer.getUrl();
bind(url, rmiCachePeer);
} catch (Exception e) {
throw new CacheException("Problem starting listener for RMICachePeer "
+ url + ". Initial cause was " + e.getMessage(), e);
}
synchronized (cachePeers) {
cachePeers.put(cacheName, rmiCachePeer);
}
}
if (LOG.isDebugEnabled()) {
LOG.debug(cachePeers.size() + " RMICachePeers bound in registry for RMI listener");
}
}
/**
* Called immediately after a cache has been disposed and removed. The calling method will block until
* this method returns.
*
* Note that the CacheManager calls this method from a synchronized method. Any attempt to call a synchronized
* method on CacheManager from this method will cause a deadlock.
*
* Note that a {@link net.sf.ehcache.event.CacheEventListener} status changed will also be triggered. Any attempt from that notification
* to access CacheManager will also result in a deadlock.
*
* @param cacheName the name of the Cache the operation relates to
*/
public void notifyCacheRemoved(String cacheName) {
LOG.debug("Removing from RMI listener", cacheName);
//don't remove if already removed.
synchronized (cachePeers) {
if (cachePeers.get(cacheName) == null) {
return;
}
}
RMICachePeer rmiCachePeer;
synchronized (cachePeers) {
rmiCachePeer = (RMICachePeer) cachePeers.remove(cacheName);
}
String url = null;
try {
unbind(rmiCachePeer);
} catch (Exception e) {
throw new CacheException("Error removing Cache Peer "
+ url + " from listener. Message was: " + e.getMessage(), e);
}
if (LOG.isDebugEnabled()) {
LOG.debug(cachePeers.size() + " RMICachePeers bound in registry for RMI listener");
}
}
/**
* Package local method for testing
*/
void addCachePeer(String name, RMICachePeer peer) {
synchronized (cachePeers) {
cachePeers.put(name, peer);
}
}
}