| /******************************************************************************* |
| * Copyright (c) 2013 Composent, Inc. and others. All rights reserved. This |
| * program and the accompanying materials are made available under the terms of |
| * the Eclipse Public License v1.0 which accompanies this distribution, and is |
| * available at http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: Composent, Inc. - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.ecf.remoteservice; |
| |
| import java.io.Serializable; |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Method; |
| import java.security.AccessController; |
| import java.security.PrivilegedExceptionAction; |
| import java.util.*; |
| import org.eclipse.ecf.core.identity.ID; |
| import org.eclipse.ecf.core.util.reflection.ClassUtil; |
| |
| /** |
| * @since 8.3 |
| */ |
| public class RemoteServiceRegistrationImpl implements IRemoteServiceRegistration, Serializable { |
| |
| private static final long serialVersionUID = -6420067298294549200L; |
| |
| transient Object service; |
| |
| /** service classes for this registration. */ |
| protected String[] clazzes; |
| |
| /** properties for this registration. */ |
| protected Properties properties; |
| |
| /** service ranking. */ |
| protected int serviceranking; |
| |
| /* internal object to use for synchronization */ |
| transient protected Object registrationLock = new Object(); |
| |
| /** The registration state */ |
| protected int state = REGISTERED; |
| |
| public static final int REGISTERED = 0x00; |
| |
| public static final int UNREGISTERING = 0x01; |
| |
| public static final int UNREGISTERED = 0x02; |
| |
| protected transient RemoteServiceReferenceImpl reference = null; |
| |
| /** |
| * @since 3.0 |
| */ |
| protected IRemoteServiceID remoteServiceID; |
| |
| protected IRegistrationListener registrationListener; |
| |
| public RemoteServiceRegistrationImpl() { |
| this.registrationListener = null; |
| } |
| |
| public RemoteServiceRegistrationImpl(IRegistrationListener listener) { |
| this.registrationListener = listener; |
| } |
| |
| public boolean equals(Object o) { |
| if (this == o) |
| return true; |
| if (o == null) |
| return false; |
| if (!(o.getClass().equals(this.getClass()))) |
| return false; |
| return getID().equals(((RemoteServiceRegistrationImpl) o).getID()); |
| } |
| |
| public int hashCode() { |
| return getID().hashCode(); |
| } |
| |
| public void publish(RemoteServiceRegistryImpl registry, Object svc, String[] clzzes, Dictionary props) { |
| this.service = svc; |
| this.clazzes = clzzes; |
| this.reference = new RemoteServiceReferenceImpl(this); |
| synchronized (registry) { |
| ID containerID = registry.getContainerID(); |
| if (containerID == null) |
| throw new NullPointerException("Local containerID must be non-null to register remote services"); //$NON-NLS-1$ |
| this.remoteServiceID = registry.createRemoteServiceID(registry.getNextServiceId()); |
| this.properties = createProperties(props); |
| registry.publishService(this); |
| } |
| } |
| |
| public Object getService() { |
| return service; |
| } |
| |
| public ID getContainerID() { |
| return (remoteServiceID == null) ? null : remoteServiceID.getContainerID(); |
| } |
| |
| protected String[] getClasses() { |
| return clazzes; |
| } |
| |
| public IRemoteServiceReference getReference() { |
| if (reference == null) { |
| synchronized (this) { |
| reference = new RemoteServiceReferenceImpl(this); |
| } |
| } |
| return reference; |
| } |
| |
| public void setProperties(Dictionary properties) { |
| synchronized (registrationLock) { |
| /* in the process of unregistering */ |
| if (state != REGISTERED) { |
| throw new IllegalStateException("Service already registered"); //$NON-NLS-1$ |
| } |
| this.properties = createProperties(properties); |
| } |
| |
| // XXX Need to notify that registration modified |
| } |
| |
| public void unregister() { |
| if (this.registrationListener != null) { |
| this.registrationListener.unregister(this); |
| } |
| } |
| |
| /** |
| * Construct a properties object from the dictionary for this |
| * ServiceRegistration. |
| * |
| * @param props |
| * The properties for this service. |
| * @return A Properties object for this ServiceRegistration. |
| */ |
| protected Properties createProperties(Dictionary props) { |
| final Properties resultProps = new Properties(props); |
| |
| resultProps.setProperty(RemoteServiceRegistryImpl.REMOTEOBJECTCLASS, clazzes); |
| |
| resultProps.setProperty(RemoteServiceRegistryImpl.REMOTESERVICE_ID, new Long(getID().getContainerRelativeID())); |
| |
| final Object ranking = (props == null) ? null : props.get(RemoteServiceRegistryImpl.REMOTESERVICE_RANKING); |
| |
| serviceranking = (ranking instanceof Integer) ? ((Integer) ranking).intValue() : 0; |
| |
| return (resultProps); |
| } |
| |
| static class Properties extends Hashtable { |
| |
| /** |
| * |
| */ |
| private static final long serialVersionUID = -3684607010228779249L; |
| |
| /** |
| * Create a properties object for the service. |
| * |
| * @param props |
| * The properties for this service. |
| */ |
| private Properties(int size, Dictionary props) { |
| super((size << 1) + 1); |
| |
| if (props != null) { |
| synchronized (props) { |
| final Enumeration keysEnum = props.keys(); |
| |
| while (keysEnum.hasMoreElements()) { |
| final Object key = keysEnum.nextElement(); |
| |
| if (key instanceof String) { |
| final String header = (String) key; |
| |
| setProperty(header, props.get(header)); |
| } |
| } |
| } |
| } |
| } |
| |
| protected Properties() { |
| super(); |
| } |
| |
| /** |
| * Create a properties object for the service. |
| * |
| * @param props |
| * The properties for this service. |
| */ |
| protected Properties(Dictionary props) { |
| this((props == null) ? 2 : Math.max(2, props.size()), props); |
| } |
| |
| /** |
| * Get a clone of the value of a service's property. |
| * |
| * @param key |
| * header name. |
| * @return Clone of the value of the property or <code>null</code> if |
| * there is no property by that name. |
| */ |
| protected Object getProperty(String key) { |
| return (cloneValue(get(key))); |
| } |
| |
| /** |
| * Get the list of key names for the service's properties. |
| * |
| * @return The list of property key names. |
| */ |
| protected synchronized String[] getPropertyKeys() { |
| final int size = size(); |
| |
| final String[] keynames = new String[size]; |
| |
| final Enumeration keysEnum = keys(); |
| |
| for (int i = 0; i < size; i++) { |
| keynames[i] = (String) keysEnum.nextElement(); |
| } |
| |
| return (keynames); |
| } |
| |
| /** |
| * Put a clone of the property value into this property object. |
| * |
| * @param key |
| * Name of property. |
| * @param value |
| * Value of property. |
| * @return previous property value. |
| */ |
| protected synchronized Object setProperty(String key, Object value) { |
| return (put(key, cloneValue(value))); |
| } |
| |
| /** |
| * Attempt to clone the value if necessary and possible. |
| * |
| * For some strange reason, you can test to see of an Object is |
| * Cloneable but you can't call the clone method since it is protected |
| * on Object! |
| * |
| * @param value |
| * object to be cloned. |
| * @return cloned object or original object if we didn't clone it. |
| */ |
| protected static Object cloneValue(Object value) { |
| if (value == null) { |
| return null; |
| } |
| if (value instanceof String) { |
| return (value); |
| } |
| |
| final Class clazz = value.getClass(); |
| if (clazz.isArray()) { |
| // Do an array copy |
| final Class type = clazz.getComponentType(); |
| final int len = Array.getLength(value); |
| final Object clonedArray = Array.newInstance(type, len); |
| System.arraycopy(value, 0, clonedArray, 0, len); |
| return clonedArray; |
| } |
| // must use reflection because Object clone method is protected!! |
| try { |
| return (clazz.getMethod("clone", (Class[]) null).invoke(value, (Object[]) null)); //$NON-NLS-1$ |
| } catch (final Exception e) { |
| /* clone is not a public method on value's class */ |
| } catch (final Error e) { |
| /* JCL does not support reflection; try some well known types */ |
| if (value instanceof Vector) { |
| return (((Vector) value).clone()); |
| } |
| if (value instanceof Hashtable) { |
| return (((Hashtable) value).clone()); |
| } |
| } |
| return (value); |
| } |
| |
| public synchronized String toString() { |
| final String keys[] = getPropertyKeys(); |
| |
| final int size = keys.length; |
| |
| final StringBuffer sb = new StringBuffer(20 * size); |
| |
| sb.append('{'); |
| |
| int n = 0; |
| for (int i = 0; i < size; i++) { |
| final String key = keys[i]; |
| if (!key.equals(RemoteServiceRegistryImpl.REMOTEOBJECTCLASS)) { |
| if (n > 0) { |
| sb.append(", "); //$NON-NLS-1$ |
| } |
| |
| sb.append(key); |
| sb.append('='); |
| final Object value = get(key); |
| if (value.getClass().isArray()) { |
| sb.append('['); |
| final int length = Array.getLength(value); |
| for (int j = 0; j < length; j++) { |
| if (j > 0) { |
| sb.append(','); |
| } |
| sb.append(Array.get(value, j)); |
| } |
| sb.append(']'); |
| } else { |
| sb.append(value); |
| } |
| n++; |
| } |
| } |
| |
| sb.append('}'); |
| |
| return (sb.toString()); |
| } |
| } |
| |
| public Object getProperty(String key) { |
| return properties.getProperty(key); |
| } |
| |
| public String[] getPropertyKeys() { |
| return properties.getPropertyKeys(); |
| } |
| |
| public long getServiceId() { |
| IRemoteServiceID rsID = getID(); |
| if (rsID == null) |
| return 0L; |
| return rsID.getContainerRelativeID(); |
| } |
| |
| private static final Object[] NULL_ARGS = new Object[0]; |
| private static final Class[] NULL_TYPES = new Class[0]; |
| |
| public static Class[] getTypesForParameters(Object args[]) { |
| Class argTypes[] = null; |
| if (args == null || args.length == 0) |
| argTypes = NULL_TYPES; |
| else { |
| argTypes = new Class[args.length]; |
| for (int i = 0; i < args.length; i++) { |
| if (args[i] == null) |
| argTypes[i] = null; |
| else |
| argTypes[i] = args[i].getClass(); |
| } |
| } |
| return argTypes; |
| } |
| |
| public Object callService(IRemoteCall call) throws Exception { |
| Object[] callArgs = call.getParameters(); |
| Object[] args = (callArgs == null) ? NULL_ARGS : callArgs; |
| final Method method = ClassUtil.getMethod(service.getClass(), call.getMethod(), getTypesForParameters(args)); |
| AccessController.doPrivileged(new PrivilegedExceptionAction() { |
| public Object run() throws Exception { |
| if (!method.isAccessible()) |
| method.setAccessible(true); |
| return null; |
| } |
| }); |
| return method.invoke(service, args); |
| } |
| |
| public String toString() { |
| StringBuffer buf = new StringBuffer("RemoteServiceRegistrationImpl["); //$NON-NLS-1$ |
| buf.append("remoteServiceID=").append(getID()).append(";"); //$NON-NLS-1$ //$NON-NLS-2$ |
| buf.append("rserviceranking=").append(serviceranking).append(";"); //$NON-NLS-1$ //$NON-NLS-2$ |
| buf.append("classes=").append(Arrays.asList(clazzes)).append(";"); //$NON-NLS-1$ //$NON-NLS-2$ |
| buf.append("state=").append(state).append(";"); //$NON-NLS-1$ //$NON-NLS-2$ |
| buf.append("properties=").append(properties).append("]"); //$NON-NLS-1$ //$NON-NLS-2$ |
| return buf.toString(); |
| } |
| |
| /** |
| * @since 3.0 |
| */ |
| public IRemoteServiceID getID() { |
| return this.remoteServiceID; |
| } |
| |
| /** |
| * @since 8.9 |
| * @return String[] the interface classes associated with this registration |
| */ |
| public String[] getInterfaces() { |
| return this.clazzes; |
| } |
| } |