Java tutorial
/* * ################################################################ * * ProActive Parallel Suite(TM): The Java(TM) library for * Parallel, Distributed, Multi-Core Computing for * Enterprise Grids & Clouds * * Copyright (C) 1997-2012 INRIA/University of * Nice-Sophia Antipolis/ActiveEon * Contact: proactive@ow2.org or contact@activeeon.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; version 3 of * the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. * * Initial developer(s): The ProActive Team * http://proactive.inria.fr/team_members.htm * Contributor(s): * * ################################################################ * $$PROACTIVE_INITIAL_DEV$$ */ package org.objectweb.proactive.core.remoteobject; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.TypeVariable; import java.net.URI; import java.rmi.dgc.VMID; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Observable; import java.util.Observer; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.log4j.Logger; import org.objectweb.proactive.core.ProActiveException; import org.objectweb.proactive.core.ProActiveRuntimeException; import org.objectweb.proactive.core.ProtocolException; import org.objectweb.proactive.core.body.reply.Reply; import org.objectweb.proactive.core.body.request.Request; import org.objectweb.proactive.core.config.CentralPAPropertyRepository; import org.objectweb.proactive.core.mop.MethodCall; import org.objectweb.proactive.core.remoteobject.benchmark.RemoteObjectBenchmark; import org.objectweb.proactive.core.remoteobject.exception.UnknownProtocolException; import org.objectweb.proactive.core.runtime.ProActiveRuntimeImpl; import org.objectweb.proactive.core.security.exceptions.RenegotiateSessionException; import org.objectweb.proactive.core.util.URIBuilder; import org.objectweb.proactive.core.util.log.Loggers; import org.objectweb.proactive.core.util.log.ProActiveLogger; public class RemoteObjectSet implements Serializable, Observer { static final Logger LOGGER_RO = ProActiveLogger.getLogger(Loggers.REMOTEOBJECT); public static final int UNREACHABLE_VALUE = Integer.MIN_VALUE; public static final int NOCHANGE_VALUE = 0; private ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock(); /** * Protocol order received from the proactive.communication.protocols.order property */ private static List<String> defaultProtocolOrder; /** * Order received when reading a stub or when creating the RemoteObjectSet locally. * The order will be used each time this RemoteObjectSet will serialized */ private List<URI> initialorder; private static Method getURI; /** * Results of the benchmarks, or if no benchmark is done, stores the natural order & the fact that some protocols are * not reachable */ private transient ConcurrentHashMap<URI, Integer> lastBenchmarkResults; // The following can be modified only atomically // *transient * Each RRO need a special marshalling processing /** * Map of all RRO, indexed by the rro uri */ private transient HashMap<URI, RemoteRemoteObject> rros; /** * Sorted list of RRO uris, according to natural order, benchmark or reachability */ private transient ArrayList<URI> sortedrros; /** * The default protocol of this remote object set */ private transient RemoteRemoteObject defaultRO; private transient URI defaultURI = null; private String remoteRuntimeName; private VMID vmid = null; /** * A forced protocol, if any */ private RemoteRemoteObject forcedProtocol = null; static { if (CentralPAPropertyRepository.PA_COMMUNICATION_PROTOCOLS_ORDER.isSet()) { defaultProtocolOrder = new ArrayList<String>( CentralPAPropertyRepository.PA_COMMUNICATION_PROTOCOLS_ORDER.getValue()); } else { defaultProtocolOrder = Collections.emptyList(); } try { getURI = InternalRemoteRemoteObject.class.getDeclaredMethod("getURI", new Class<?>[0]); } catch (NoSuchMethodException e) { e.printStackTrace(); } } public RemoteObjectSet(RemoteRemoteObject defaultRRO, Collection<RemoteRemoteObject> rros) throws IOException { this.rros = new LinkedHashMap<URI, RemoteRemoteObject>(); try { this.defaultRO = defaultRRO; this.remoteRuntimeName = getPARuntimeName(defaultRO); this.defaultURI = getURI(defaultRO); this.rros.put(defaultURI, defaultRO); this.sortedrros = new ArrayList<URI>(); this.sortedrros.add(defaultURI); this.initialorder = new ArrayList<URI>(); this.initialorder.add(defaultURI); this.lastBenchmarkResults = new ConcurrentHashMap<URI, Integer>(); for (RemoteRemoteObject rro : rros) { this.add(rro); } sortProtocolsInternal(); if (LOGGER_RO.isDebugEnabled()) { LOGGER_RO.debug("[ROAdapter] created RemoteObjectSet : " + sortedrros); } } catch (RemoteRemoteObjectException e) { throw new IOException( "[ROAdapter] Cannot access the remoteObject " + defaultRRO + " : " + e.getMessage()); } } /** * Select the best suited RemoteRemoteObject (protocol related), and send it the Request * Fallback to default (according to the PA_COMMUNICATION_PROTOCOL property) if necessary */ public Reply receiveMessage(Request message) throws ProActiveException, RenegotiateSessionException, IOException { if (forcedProtocol != null) { return forcedProtocol.receiveMessage(message); } RemoteRemoteObject rro = null; // the order is cloned to allow asynchronous updates by the benchmark threads ReentrantReadWriteLock.ReadLock rl = rwlock.readLock(); rl.lock(); ArrayList<URI> cloned = (ArrayList<URI>) sortedrros.clone(); rl.unlock(); // For each protocol already selected and sorted Throwable defaultProtocolException = null; MutableBoolean anyException = new MutableBoolean(false); Reply reply = null; for (URI uri : cloned) { rro = rros.get(uri); if (LOGGER_RO.isDebugEnabled()) { LOGGER_RO.debug("[ROAdapter] Sending message " + message + " to " + uri); } try { reply = rro.receiveMessage(message); // These Exceptions happened on client side // RMI doesn't act as others protocols and Exceptions aren't // encapsulated, so they are caught here. } catch (ProtocolException pae) { defaultProtocolException = handleProtocolException(pae, uri, cloned.size() > 1, anyException); } catch (IOException io) { defaultProtocolException = handleProtocolException(io, uri, cloned.size() > 1, anyException); } catch (RenegotiateSessionException rse) { defaultProtocolException = handleProtocolException(rse, uri, cloned.size() > 1, anyException); } if (reply != null) { // The Exception is thrown on server side // So it is encapsulated to be delivered on client side Throwable t = reply.getResult().getException(); if (t != null && (t instanceof ProtocolException || t instanceof IOException || t instanceof RenegotiateSessionException)) { defaultProtocolException = handleProtocolException(t, uri, cloned.size() > 1, anyException); continue; } break; } } // if we arrive to this point either a reply has been received or all protocols sent exceptions // if there has been any exception we sort the uri list before sending back the result if (anyException.getValue()) { sortProtocolsInternal(); } // In case all protocols led to Exception, simply throw the Exception sent by the default protocol if (reply == null && defaultProtocolException != null) { if (defaultProtocolException instanceof ProtocolException) { throw (ProtocolException) defaultProtocolException; } else if (defaultProtocolException instanceof IOException) { throw (IOException) defaultProtocolException; } else if (defaultProtocolException instanceof RenegotiateSessionException) { throw (RenegotiateSessionException) defaultProtocolException; } } // otherwise, we received a reply return reply; } // Handles the Exceptions received in the receiveMessage method, doing a special treatment for the default protocol private Throwable handleProtocolException(Throwable e, URI uri, boolean multiProtocol, MutableBoolean anyException) { anyException.setValue(true); if (!uri.equals(defaultURI)) { LOGGER_RO.warn("[ROAdapter] Disabling protocol " + uri.getScheme() + " because of received exception", e); lastBenchmarkResults.put(uri, UNREACHABLE_VALUE); return null; } else { if (multiProtocol) { LOGGER_RO.warn("[ROAdapter] Skipping default protocol " + uri.getScheme() + " because of received exception", e); } lastBenchmarkResults.put(uri, UNREACHABLE_VALUE); return e; } } /** * Force the specified protocol to be used for all communication. Null value avoid the forcing. * * @throws UnknownProtocolException * The protocol specified isn't known * @throws NotYetExposedException * The Object isn't already exposed with this protocol */ public void forceProtocol(String protocol) throws UnknownProtocolException, NotYetExposedException { if (protocol == null || protocol.length() == 0) { this.forcedProtocol = null; return; } // Protocols factories can be added dynamically, so the only way to // check a protocol existence is to check if it have a factory if (RemoteObjectProtocolFactoryRegistry.get(protocol) != null) { boolean exposed = false; for (URI uri : rros.keySet()) { if (uri.getScheme().equalsIgnoreCase(protocol)) { this.forcedProtocol = rros.get(uri); exposed = true; } } if (!exposed) { throw new NotYetExposedException("The object isn't exposed on protocol " + protocol); } } else { throw new UnknownProtocolException("\"" + protocol + "\"" + " isn't a valid protocol."); } } /** * Return the default RemoteRemoteObject */ public RemoteRemoteObject getDefault() { return this.defaultRO; } /** * Return the URI of the default RemoteRemoteObject */ public URI getDefaultURI() throws ProActiveException { return this.defaultURI; } /** * Sort the list of rro uris, using locks to prevent concurrent modification */ private void sortProtocolsInternal() { ReentrantReadWriteLock.WriteLock wl = rwlock.writeLock(); wl.lock(); sortedrros = sortProtocols(rros.keySet(), defaultProtocolOrder, lastBenchmarkResults, defaultURI); wl.unlock(); } /** * Helper method used to sort the list of protocols * @param input * @param defOrder * @param benchmarkRes * @param defUri * @return */ public static ArrayList<URI> sortProtocols(Collection<URI> input, final List<String> defOrder, final ConcurrentHashMap<URI, Integer> benchmarkRes, final URI defUri) { ArrayList<URI> output = new ArrayList<URI>(); output.addAll(input); Collections.sort(output, new Comparator<URI>() { @Override public int compare(URI o1, URI o2) { // unreachable uri, they are put at the end of the list if (benchmarkRes.containsKey(o1) && benchmarkRes.get(o1) == UNREACHABLE_VALUE) { return 1; } if (benchmarkRes.containsKey(o2) && benchmarkRes.get(o2) == UNREACHABLE_VALUE) { return -1; } // sort accordingly to fixed order if (defOrder.contains(o1.getScheme()) && defOrder.contains(o2.getScheme())) { return defOrder.indexOf(o1.getScheme()) - defOrder.indexOf(o2.getScheme()); } // the following code means that any protocol present in the default order // is preferred to any other protocol, currently this behavior is deactivated if (defOrder.contains(o1.getScheme())) { return -1; } if (defOrder.contains(o2.getScheme())) { return 1; } if (benchmarkRes.containsKey(o1) && benchmarkRes.containsKey(o2)) { // sort accordingly to benchmark results if (benchmarkRes.get(o1) > benchmarkRes.get(o2)) { return -1; } else if (benchmarkRes.get(o2) > benchmarkRes.get(o1)) { return 1; } return 0; } // undetermined, we have no info return 0; } }); // finally remove unreachable protocols for (ListIterator<URI> it = output.listIterator(output.size()); it.hasPrevious();) { URI reachableOrNot = it.previous(); if (benchmarkRes.containsKey(reachableOrNot) && benchmarkRes.get(reachableOrNot) == UNREACHABLE_VALUE) { if (!reachableOrNot.equals(defUri)) { it.remove(); } } else { // we exit the loop at the first reachable protocol break; } } return output; } /** * Add a RemoteRemoteObject (protocol specific) to the RemoteObjectSet * If it is unreliable, keep it aside for later possible use */ public void add(RemoteRemoteObject rro) { try { URI uri = getURI(rro); this.rros.put(uri, rro); this.sortedrros.add(uri); this.initialorder.add(uri); } catch (RemoteRemoteObjectException e) { LOGGER_RO.warn(e); } } /** * @see org.objectweb.proactive.core.remoteobject.RemoteObjectSet#add(RemoteRemoteObject) */ public void add(Collection<RemoteRemoteObject> rros) { // If an older same rro is present, it will be updated for (RemoteRemoteObject rro : rros) { this.add(rro); } } /** * Send a non-functional internal request to get the URI of the RemoteRemoteObject */ private URI getURI(RemoteRemoteObject rro) throws RemoteRemoteObjectException { try { MethodCall mc = MethodCall.getMethodCall(getURI, new Object[0], new HashMap<TypeVariable<?>, Class<?>>()); Request r = new InternalRemoteRemoteObjectRequest(mc); Reply rep = rro.receiveMessage(r); return (URI) rep.getResult().getResult(); } catch (ProActiveException e) { throw new RemoteRemoteObjectException("RemoteObjectSet: can't access RemoteObject through " + rro, e); } catch (IOException e) { throw new RemoteRemoteObjectException("RemoteObjectSet: can't access RemoteObject through " + rro, e); } catch (ProActiveRuntimeException e) { throw new RemoteRemoteObjectException("RemoteObjectSet: can't access RemoteObject through " + rro, e); } catch (RenegotiateSessionException e) { LOGGER_RO.error("", e); throw new RemoteRemoteObjectException(e); } } /** * Send a non-functional internal request to get the name of the remote ProActiveRuntime */ private String getPARuntimeName(RemoteRemoteObject rro) throws RemoteRemoteObjectException { try { Request r = new PARuntimeNameRequest(); Reply rep = rro.receiveMessage(r); return (String) rep.getResult().getResult(); } catch (ProActiveException e) { throw new RemoteRemoteObjectException("RemoteObjectSet: can't get ProActiveRuntime urls from " + rro, e); } catch (IOException e) { throw new RemoteRemoteObjectException("RemoteObjectSet: can't get ProActiveRuntime urls from " + rro, e); } catch (RenegotiateSessionException e) { LOGGER_RO.error("", e); throw new RemoteRemoteObjectException(e); } } /** * Exception thrown an communication error, internal use only */ public class RemoteRemoteObjectException extends Exception { RemoteRemoteObjectException(Exception e) { super(e); } RemoteRemoteObjectException(String m) { super(m); } RemoteRemoteObjectException(String m, Exception e) { super(m, e); } } /** * The Object isn't already exposed with this protocol */ public class NotYetExposedException extends Exception { public NotYetExposedException(Exception e) { super(e); } public NotYetExposedException(String m) { super(m); } public NotYetExposedException(String m, Exception e) { super(m, e); } } public int size() { return this.rros.size(); } /** * Network topology could have change, change the order */ private void startBenchmark() { // The update of the order is done asynchronously if (CentralPAPropertyRepository.PA_BENCHMARK_ACTIVATE.isTrue()) { if (rros.size() > 1) RemoteObjectBenchmark.getInstance().subscribeAsObserver(this, rros, this.remoteRuntimeName, lastBenchmarkResults); } } /** * Update the protocol order from the new ProActive Runtime * when the remote remote object is reified */ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); int size = in.readInt(); ReentrantReadWriteLock.WriteLock wl = rwlock.writeLock(); wl.lock(); this.rros = new LinkedHashMap<URI, RemoteRemoteObject>(size); this.sortedrros = new ArrayList<URI>(); this.lastBenchmarkResults = new ConcurrentHashMap<URI, Integer>(); // read protocols for (int i = 0; i < size; i++) { Map.Entry<URI, RemoteRemoteObject> entry = readProtocol(in); if (entry != null) { URI uri = entry.getKey(); RemoteRemoteObject rro = entry.getValue(); if (i == 0) { // default protocol is the first one this.defaultURI = uri; this.defaultRO = rro; } this.rros.put(uri, rro); sortedrros.add(uri); lastBenchmarkResults.put(uri, size - i); } } wl.unlock(); sortProtocolsInternal(); if (LOGGER_RO.isDebugEnabled()) { LOGGER_RO.debug("[ROAdapter] read RemoteObjectSet " + sortedrros); } VMID testLocal = ProActiveRuntimeImpl.getProActiveRuntime().getVMInformation().getVMID(); if (!vmid.equals(testLocal)) { this.vmid = testLocal; this.startBenchmark(); } } private void writeObject(ObjectOutputStream out) throws IOException { // Almost same as in term of speed UniqueID.getCurrentVMID() but more readable this.vmid = ProActiveRuntimeImpl.getProActiveRuntime().getVMInformation().getVMID(); out.defaultWriteObject(); out.writeInt(rros.size()); // write the default protocol writeProtocol(out, defaultURI, defaultRO); // write all other protocols for (URI uri : initialorder) { if (!uri.equals(defaultURI)) { writeProtocol(out, uri, rros.get(uri)); } } } private Map.Entry<URI, RemoteRemoteObject> readProtocol(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { ObjectInputStream ois = null; URI uri; RemoteRemoteObject rro; try { // Read the data before calling any method throwing an exception to avoid stream corruption uri = (URI) in.readObject(); byte[] buf = (byte[]) in.readObject(); if (buf != null) { RemoteObjectFactory rof = AbstractRemoteObjectFactory.getRemoteObjectFactory(uri.getScheme()); ois = rof.getProtocolObjectInputStream(new ByteArrayInputStream(buf)); rro = (RemoteRemoteObject) ois.readObject(); } else { LOGGER_RO.debug("Sender was unable to serialize RemoteRemoteObject for " + uri); return null; } } catch (UnknownProtocolException e) { LOGGER_RO.debug("Failed to instanciate a ROF when receiving a RemoteObjectset", e); return null; } finally { if (ois != null) ois.close(); } return new AbstractMap.SimpleEntry<URI, RemoteRemoteObject>(uri, rro); } private void writeProtocol(ObjectOutputStream out, URI uri, RemoteRemoteObject rro) throws IOException { String scheme = uri.getScheme(); byte[] buf = null; ObjectOutputStream oos = null; try { RemoteObjectFactory rof = AbstractRemoteObjectFactory.getRemoteObjectFactory(scheme); ByteArrayOutputStream baos = new ByteArrayOutputStream(); oos = rof.getProtocolObjectOutputStream(baos); oos.writeObject(rro); oos.flush(); buf = baos.toByteArray(); } catch (UnknownProtocolException e) { LOGGER_RO.warn("[ROAdapter] Failed to serialize the RemoteRemoteObject for " + uri); } finally { if (oos != null) oos.close(); } out.writeObject(uri); out.writeObject(buf); // null if serialization failed } /** * Notification from a BenchmarkMonitorThread Object */ public void update(Observable o, Object arg) { ReentrantReadWriteLock.WriteLock wl = rwlock.writeLock(); wl.lock(); lastBenchmarkResults.putAll((Map<URI, Integer>) arg); sortProtocolsInternal(); if (LOGGER_RO.isDebugEnabled()) { LOGGER_RO.debug("[Multi-Protocol] " + URIBuilder.getNameFromURI(defaultURI) + " received protocol order: " + sortedrros); } wl.unlock(); } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); for (URI uri : rros.keySet()) { sb.append(uri.toString()); sb.append(", "); } sb.delete(sb.length() - 2, sb.length()); sb.append("]"); return sb.toString(); } }