blob: 63e196e8195ee4f1b6b279fd06e66e3a104f5652 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}