package org.distributeme.core; import net.anotheria.util.BasicComparable; import java.io.Serializable; import java.util.Calendar; /** * This class represents a resolvable address of a service. * A server address is build up like following: * <protocol>://<serviceid>.<instanceid>@<host>:<port> * * @author lrosenberg * @version $Id: $Id */ public class ServiceDescriptor implements Serializable, Cloneable{ /** * serial version uid. */ private static final long serialVersionUID = 2854220693966853387L; /** * Separator between protocol part of the descriptor and the rest of the descriptor. */ public static final String PROTOCOL_SEPARATOR = "://"; /** * Separator between host part and interface part in the string representation. */ public static final char HOST_SEPARATOR = '@'; /** * Separator between host and port. */ public static final char PORT_SEPARATOR = ':'; /** * Separator between serviceId and instanceId. */ public static final char INSTANCE_SEPARATOR = '.'; /** * Separator between port and startup timestamp. */ public static final char TIMESTAMP_SEPARATOR = '@'; /** * Supported protocols. * @author lrosenberg * */ public static enum Protocol{ /** * RMI */ RMI, /** * JAXWS aka WebService. */ JAXWS, /** * CORBA. */ CORBA, /** * Jax Rest Service. */ JAXRS } /** * The protocol this instance supports. */ private Protocol protocol; /** * The id of the service - usually a string delivered from the interface and package name. */ private String serviceId; /** * Host on which the service is running. */ private String host; /** * Port on which the local rmi registry is running. */ private int port; /** * A unique id (random generated) which is only valid for the live time of the one instance. */ private String instanceId; /** * Timestamp in milliseconds. */ private long timestamp; /** * This constructor is used for registration. * * @param aProtocol a {@link org.distributeme.core.ServiceDescriptor.Protocol} object. * @param aServiceId a {@link java.lang.String} object. * @param anInstanceId a {@link java.lang.String} object. * @param aHost a {@link java.lang.String} object. * @param aPort a int. */ public ServiceDescriptor(Protocol aProtocol, String aServiceId, String anInstanceId, String aHost, int aPort){ this(aProtocol, aServiceId, anInstanceId, aHost, aPort, 0); } /** * <p>Constructor for ServiceDescriptor.</p> * * @param aProtocol a {@link org.distributeme.core.ServiceDescriptor.Protocol} object. * @param aServiceId a {@link java.lang.String} object. * @param anInstanceId a {@link java.lang.String} object. * @param aHost a {@link java.lang.String} object. * @param aPort a int. * @param aTimestamp a long. */ public ServiceDescriptor(Protocol aProtocol, String aServiceId, String anInstanceId, String aHost, int aPort, long aTimestamp){ if (aProtocol==null) throw new IllegalArgumentException("Null protocol is not allowed"); if (aServiceId==null || aServiceId.equals("")) throw new IllegalArgumentException("Null or empty serviceId is not allowed"); if (anInstanceId==null || anInstanceId.equals("")) throw new IllegalArgumentException("Null or empty instanceId is not allowed"); if (aHost==null || aHost.equals("")) throw new IllegalArgumentException("Null or empty host is not allowed"); protocol = aProtocol; serviceId = aServiceId; instanceId = anInstanceId; host = aHost; port = aPort; timestamp = (aTimestamp > 0) ? aTimestamp : System.currentTimeMillis(); } /** * This constructor is used for the lookup. * * @param aProtocol a {@link org.distributeme.core.ServiceDescriptor.Protocol} object. * @param aServiceId a {@link java.lang.String} object. */ public ServiceDescriptor(Protocol aProtocol, String aServiceId){ if (aProtocol==null) throw new IllegalArgumentException("Null protocol is not allowed"); if (aServiceId==null || aServiceId.equals("")) throw new IllegalArgumentException("Null or empty serviceId is not allowed"); protocol = aProtocol; serviceId = aServiceId; } /** * <p>getGlobalServiceId.</p> * * @return a {@link java.lang.String} object. */ public String getGlobalServiceId(){ return protocol.toString().toLowerCase()+"://"+serviceId; } /** * <p>getRegistrationString.</p> * * @return a {@link java.lang.String} object. */ public String getRegistrationString(){ return getGlobalServiceId()+INSTANCE_SEPARATOR+instanceId+HOST_SEPARATOR+host+PORT_SEPARATOR+port+TIMESTAMP_SEPARATOR+getTimeString(timestamp); } /** * <p>getLookupString.</p> * * @return a {@link java.lang.String} object. */ public String getLookupString(){ return getGlobalServiceId(); } /** {@inheritDoc} */ @Override public String toString(){ return getRegistrationString(); } /** * <p>getSystemWideUniqueId.</p> * * @return a {@link java.lang.String} object. */ public final String getSystemWideUniqueId(){ return getRegistrationString(); } /** * <p>Getter for the field <code>timestamp</code>.</p> * * @return a long. */ public long getTimestamp() { return timestamp; } /** * Virtually the same as <b>fromRegistrationString</b> but better named ;-). * * @param systemWideUniqueId a {@link java.lang.String} object. * @return a {@link org.distributeme.core.ServiceDescriptor} object. */ public static final ServiceDescriptor fromSystemWideUniqueId(String systemWideUniqueId){ return fromRegistrationString(systemWideUniqueId); } /** * Factory method to create a service descriptor from a registration string (used for bind in the registry). * * @param registrationString a {@link java.lang.String} object. * @return a {@link org.distributeme.core.ServiceDescriptor} object. */ public static final ServiceDescriptor fromRegistrationString(String registrationString){ int indexOfHost = registrationString.indexOf(HOST_SEPARATOR); long timestamp = 0; int indexOfTimestamp = registrationString.substring(indexOfHost+1).indexOf(TIMESTAMP_SEPARATOR); if(indexOfTimestamp >= 0) { indexOfTimestamp += indexOfHost + 1; timestamp = parseTimeString(registrationString.substring(indexOfTimestamp + 1)); } else indexOfTimestamp = registrationString.length(); String hostAndPort = registrationString.substring(indexOfHost+1, indexOfTimestamp); String protocolAndServiceId = registrationString.substring(0, indexOfHost); int portSeparatorPos = hostAndPort.lastIndexOf(PORT_SEPARATOR); String host = hostAndPort.substring(0, portSeparatorPos); String port = hostAndPort.substring(portSeparatorPos + 1, hostAndPort.length()); String protocol = protocolAndServiceId.substring(0, protocolAndServiceId.indexOf(':')); String serviceAndInstanceId = protocolAndServiceId.substring(protocolAndServiceId.indexOf('/')+2); int indexOfInstanceIdSeparator = serviceAndInstanceId.lastIndexOf(INSTANCE_SEPARATOR); String serviceId = serviceAndInstanceId.substring(0, indexOfInstanceIdSeparator); String instanceId = serviceAndInstanceId.substring(indexOfInstanceIdSeparator+1); return new ServiceDescriptor(Protocol.valueOf(protocol.toUpperCase()), serviceId, instanceId, host, Integer.parseInt(port), timestamp); } /** * Factory method to create a service descriptor from a resolve string (used for lookup in the registry). * * @param resolveString a {@link java.lang.String} object. * @return a {@link org.distributeme.core.ServiceDescriptor} object. */ public static final ServiceDescriptor fromResolveString(String resolveString){ String protocol = resolveString.substring(0, resolveString.indexOf(':')); String serviceId = resolveString.substring(resolveString.indexOf('/')+2); return new ServiceDescriptor(Protocol.valueOf(protocol.toUpperCase()), serviceId); } /** * Changes the service id in the descriptor to another id. This is useful if you want to access a not publicly registered service (like eventservice or lifecycle servce) * via the public service id from the registry. * * @return new service descriptor with altered service id. * @param aServiceId a {@link java.lang.String} object. */ public ServiceDescriptor changeServiceId(String aServiceId){ try{ ServiceDescriptor newSD = (ServiceDescriptor)this.clone(); newSD.serviceId = aServiceId; return newSD; }catch(CloneNotSupportedException e){ throw new AssertionError("Can't happen"); } } /** {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((host == null) ? 0 : host.hashCode()); result = prime * result + port; result = prime * result + ((protocol == null) ? 0 : protocol.hashCode()); result = prime * result + ((serviceId == null) ? 0 : serviceId.hashCode()); result = prime * result + ((instanceId == null) ? 0 : instanceId.hashCode()); return result; } /** {@inheritDoc} */ @Override public boolean equals(Object o){ return o instanceof ServiceDescriptor ? protocol == ((ServiceDescriptor)o).protocol && port == ((ServiceDescriptor)o).port && BasicComparable.compareString(host, ((ServiceDescriptor)o).host)==0 && BasicComparable.compareString(serviceId, ((ServiceDescriptor)o).serviceId)==0 && BasicComparable.compareString(instanceId, ((ServiceDescriptor)o).instanceId)==0 : false; } /** * <p>Getter for the field <code>host</code>.</p> * * @return a {@link java.lang.String} object. */ public String getHost(){ return host; } /** * <p>Getter for the field <code>port</code>.</p> * * @return a int. */ public int getPort(){ return port; } /** * <p>Getter for the field <code>protocol</code>.</p> * * @return a {@link java.lang.String} object. */ public String getProtocol(){ return protocol.toString().toLowerCase(); } /** * <p>Getter for the field <code>serviceId</code>.</p> * * @return a {@link java.lang.String} object. */ public String getServiceId(){ return serviceId; } /** * <p>Getter for the field <code>instanceId</code>.</p> * * @return a {@link java.lang.String} object. */ public String getInstanceId() { return instanceId; } /** * <p>getTimeString.</p> * * @param timestamp a long. * @return a {@link java.lang.String} object. */ public static String getTimeString(long timestamp) { assert timestamp > 0; Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(timestamp); return String.format("%04d%02d%02d%02d%02d%02d", calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND)); } /** * <p>parseTimeString.</p> * * @param s a {@link java.lang.String} object. * @return a long. */ public static long parseTimeString(String s) { assert s != null; // Number of characters per component final int[] numChars = {4, 2, 2, 2, 2, 2}; // yyyyMMDDHHmmss // Component values int[] vals = new int[numChars.length]; Calendar calendar = Calendar.getInstance(); try { for(int i = 0, j = 0; i < numChars.length; j += numChars[i], ++i) vals[i] = Integer.parseInt(s.substring(j, j + numChars[i]), 10); } catch(IndexOutOfBoundsException e) { return 0; } catch(NumberFormatException e) { return 0; } calendar.set(vals[0], vals[1] - 1, vals[2], vals[3], vals[4], vals[5]); return calendar.getTimeInMillis(); } }