[Mulgara-svn] r722 - trunk/src/jar/server-rmi/java/org/mulgara/server/rmi
pag at mulgara.org
pag at mulgara.org
Fri Mar 28 21:34:46 UTC 2008
Author: pag
Date: 2008-03-28 14:34:45 -0700 (Fri, 28 Mar 2008)
New Revision: 722
Added:
trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/Handler.java
trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/RmiURLConnection.java
Modified:
trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/RmiServer.java
Log:
A new URL handler for the rmi protocol. This allows rmi:// URIs to be created as URLs instead, if desired. The RmiURLConnection also has some useful features for connecting to an RMI server, either to get a description of the service, or to obtain a Remote object reference that can be used directly.
Added: trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/Handler.java
===================================================================
--- trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/Handler.java (rev 0)
+++ trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/Handler.java 2008-03-28 21:34:45 UTC (rev 722)
@@ -0,0 +1,41 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.server.rmi;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+/**
+ * This Handler permits URLs with an RMI scheme to be created. It also refers an
+ * {@link #openConnection(URL)} request to the {@link RmiURLConnection} class.
+ *
+ * @created Mar 28, 2008
+ * @author Paul Gearon
+ * @copyright © 2007 <a href="mailto:pgearon at users.sourceforge.net">Paul Gearon</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class Handler extends URLStreamHandler {
+
+ /**
+ * @see java.net.URLStreamHandler#openConnection(java.net.URL)
+ */
+ @Override
+ protected URLConnection openConnection(URL u) throws IOException {
+ URLConnection connection = new RmiURLConnection(u);
+ connection.connect();
+ return connection;
+ }
+
+}
Modified: trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/RmiServer.java
===================================================================
--- trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/RmiServer.java 2008-03-28 21:32:30 UTC (rev 721)
+++ trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/RmiServer.java 2008-03-28 21:34:45 UTC (rev 722)
@@ -55,6 +55,18 @@
/** Logger. This is named after the classname. */
private final static Logger logger = Logger.getLogger(RmiServer.class.getName());
+ /** The name of the URL protocol handler package property. */
+ private static final String PROTOCOL_HANDLER = "java.protocol.handler.pkgs";
+
+ // Initialize the system to know about RMI URLs
+ static {
+ String handler = System.getProperty(PROTOCOL_HANDLER);
+ String thisPackage = RmiServer.class.getPackage().getName();
+ assert thisPackage.endsWith(".rmi");
+ String parentPackage = thisPackage.substring(0, thisPackage.lastIndexOf('.'));
+ System.setProperty(PROTOCOL_HANDLER, (handler == null) ? parentPackage : handler + "|" + parentPackage);
+ }
+
/** The default port used for RMI. */
public static final int DEFAULT_PORT = 1099;
Added: trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/RmiURLConnection.java
===================================================================
--- trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/RmiURLConnection.java (rev 0)
+++ trunk/src/jar/server-rmi/java/org/mulgara/server/rmi/RmiURLConnection.java 2008-03-28 21:34:45 UTC (rev 722)
@@ -0,0 +1,261 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.server.rmi;
+
+import java.util.Hashtable;
+import java.net.URL;
+import java.net.URLConnection;
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.rmi.UnmarshalException;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.mulgara.util.ClassDescriber;
+import org.mulgara.util.ClassDescriberXML;
+
+/**
+ * This class represents a connection to an RMI server. Reading from this connection
+ * results in an Object coming across the connection, or an XML string to describe
+ * what could have come over if the RMI deserializer cannot find a local class to
+ * instantiate the object.
+ *
+ * @created Mar 28, 2008
+ * @author Paul Gearon
+ * @copyright © 2007 <a href="mailto:pgearon at users.sourceforge.net">Paul Gearon</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class RmiURLConnection extends URLConnection {
+
+ /** The context of the registry containing the RMI references. */
+ private Context rmiRegistryContext = null;
+
+ /** A string description of the object referenced by the URL. */
+ private String description = null;
+
+ /** An object representing what the URL refers to. If possible, this is a remote object, else a String. */
+ private Object content = null;
+
+ /** Contains the raw bytes representing the data that will be "read" from this connection. */
+ private byte[] buffer = null;
+
+ /**
+ * Build a URLConnection to an RMI server.
+ * @param url The URL to use for locating the service on the server.
+ */
+ protected RmiURLConnection(URL url) {
+ super(url);
+ setDefaultUseCaches(false);
+ setDefaultAllowUserInteraction(false);
+ }
+
+ /**
+ * This type of connection does not permit user interaction.
+ * @see java.net.URLConnection#setAllowUserInteraction(boolean)
+ */
+ public void setAllowUserInteraction(boolean allow) {
+ if (allow) throw new IllegalStateException("Not valid to interact with an rmi connection");
+ }
+
+ /**
+ * Caching is not valid for this kind of connection.
+ * @see java.net.URLConnection#setUseCaches(boolean)
+ */
+ public void setUseCaches(boolean use) {
+ if (use) throw new IllegalStateException("Not valid to cache an rmi connection");
+ }
+
+ /**
+ * Connects to the specified service, and gets what content it can.
+ * @see java.net.URLConnection#connect()
+ */
+ public void connect() throws IOException {
+ Hashtable<String,String> environment = new Hashtable<String,String>();
+ environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
+ environment.put(Context.PROVIDER_URL, url.getProtocol() + "://" + url.getAuthority());
+ String name = url.getPath().substring(1);
+ try {
+ rmiRegistryContext = new InitialContext(environment);
+ updateContent(name);
+ } catch (NamingException ne) {
+ ne.printStackTrace();
+ throw new IOException("Unable to establish connection: " + ne);
+ }
+ }
+
+ /**
+ * Reads the number of bytes for the content that can be read from this connection.
+ * @return The number of bytes that can be returned from {@link #getContent()}.
+ * @see java.net.URLConnection#getContentLength()
+ */
+ public int getContentLength() {
+ return (buffer == null) ? 0 : buffer.length;
+ }
+
+ /**
+ * Gets the data representing the service connected to.
+ * @return Either a remote object, accessed through RMI, or a String containing XML if
+ * that object cannot be instantiated.
+ * @throws IOException If not connected.
+ * @see java.net.URLConnection#getContent()
+ */
+ public Object getContent() throws IOException {
+ if (content == null) throw new IOException("Not connected");
+ return content;
+ }
+
+ /**
+ * Gets the data representing the service connected to. Using a list of desired classes
+ * to specify the desired return type. The class array will be searched for a match with
+ * the following, with the first match being the returned type:
+ * <ol><li>The class of the Remote object obtained through RMI.</li>
+ * <li>{@link java.lang.String}: the result will contain an XML description of the object.</li>
+ * <li>An array of <code>byte</code>: the result will contain a serialization of either
+ * the remote object or the XML description if the remote object cannot be instantiated.</li></ol>
+ * Otherwise, a <code>null</code> is returned.
+ * @return Either a remote object, accessed through RMI, or a String containing XML if
+ * that object cannot be instantiated.
+ * @throws IOException If not connected.
+ * @see java.net.URLConnection#getContent()
+ */
+ @SuppressWarnings("unchecked")
+ public Object getContent(Class[] cls) throws IOException {
+ if (content == null) throw new IOException("Not connected");
+ if (testType(content.getClass(), cls)) return content;
+ if (testType(String.class, cls)) return description;
+ if (testType(byte[].class, cls)) return buffer;
+ return null;
+ }
+
+ /**
+ * Gets an input stream to read bytes from this connection.
+ * @return an input stream that will return the data of the content.
+ * @throws IOException If not connected.
+ * @see java.net.URLConnection#getInputStream()
+ */
+ public InputStream getInputStream() throws IOException {
+ if (buffer == null) throw new IOException("Not connected");
+ return new ByteArrayInputStream(buffer);
+ }
+
+ /**
+ * Utility to check if a class or one of its supertypes is present in an array.
+ * @param clazz The class to check for.
+ * @param cls The array of classes to test for the presence of clazz or one of its supertypes.
+ * @return <code>true</code> if the class or one of its supertypes is present in cls.
+ */
+ private static boolean testType(Class<?> clazz, Class<?>[] cls) {
+ for (Class<?> c: cls) if (c.isAssignableFrom(clazz)) return true;
+ return false;
+ }
+
+ /**
+ * Gets data from a server by name, and creates content at this side to represent it.
+ * Binary content will be created if possible, but a string containing an XML representation
+ * will always be created.
+ * @param name The name of the RMI service to get from the server.
+ * @throws NamingException Looking up the name on the server failed.
+ * @throws IOException Transfering data from the server failed.
+ */
+ private void updateContent(String name) throws NamingException, IOException {
+ if (name.length() == 0) {
+ // server only
+ description = getContextDescription(false);
+ content = description;
+ buffer = description.getBytes();
+ } else {
+ // service name. Get the object.
+ try {
+ Object content = rmiRegistryContext.lookup(name);
+ description = getServiceDescription(content);
+ buffer = (content instanceof Serializable) ? serialize((Serializable)content) : description.getBytes();
+ } catch (NamingException ne) {
+ Throwable e = ne.getCause();
+ if (!(e instanceof UnmarshalException)) throw ne;
+ if (e == null || !(e.getCause() instanceof ClassNotFoundException)) throw ne;
+ description = getContextDescription(true);
+ content = description;
+ buffer = description.getBytes();
+ }
+ }
+ }
+
+ /**
+ * Create an XML description of a {@link javax.naming.Context} from the server.
+ * @param outOfScope <code>true</code> if the class for this context is not in the classpath.
+ * @return An XML string describing the current RMI context.
+ * @throws NamingException If the RMI context could not be accessed.
+ */
+ private String getContextDescription(boolean outOfScope) throws NamingException {
+ StringBuilder dsc = getHeader();
+ dsc.append("<context name=\"").append(rmiRegistryContext.getNameInNamespace()).append("\">\n");
+ NamingEnumeration<NameClassPair> ne = rmiRegistryContext.list("");
+ while (ne.hasMore()) {
+ NameClassPair nc = ne.next();
+ dsc.append(" <name value=\"").append(nc.getName());
+ if (nc.isRelative()) dsc.append("\" relative=\"").append(nc.isRelative());
+ dsc.append("\">\n");
+
+ dsc.append(" <class name=\"").append(nc.getClassName()).append("\"");
+ if (outOfScope) dsc.append(" inscope=\"false\"");
+ dsc.append("/>\n </name>\n");
+ }
+ dsc.append("</context>");
+ return dsc.toString();
+ }
+
+ /**
+ * Create an XML description of an RMI service.
+ * @param obj The Remote RMI object representing the service.
+ * @return An XML string describing the service class.
+ * @throws NamingException If the RMI data could not be read.
+ */
+ private String getServiceDescription(Object obj) throws NamingException {
+ ClassDescriber cd = new ClassDescriberXML(obj);
+ StringBuilder b = getHeader().append("<service>\n");
+ b.append(cd.getDescription(1));
+ b.append("</service>");
+ return b.toString();
+ }
+
+ /**
+ * Creates a {@link java.lang.StringBuilder} containing an XML header. This is ready to have
+ * more data appended to it.
+ * @return A new StringBuilder with the header in it.
+ */
+ private static StringBuilder getHeader() {
+ StringBuilder header = new StringBuilder("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n\n");
+ return header;
+ }
+
+ /**
+ * Convert an object into an array of bytes.
+ * @param obj The object to serialize.
+ * @return a <code>byte[]</code> containing the serialization of obj.
+ * @throws IOException If there was an internal serialization error.
+ */
+ private byte[] serialize(Serializable obj) throws IOException {
+ ByteArrayOutputStream dataOut = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(dataOut);
+ out.writeObject(obj);
+ return dataOut.toByteArray();
+ }
+
+}
More information about the Mulgara-svn
mailing list