/**
* OpenSpotLight - Open Source IT Governance Platform
*
* Copyright (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA
* or third-party contributors as indicated by the @author tags or express
* copyright attribution statements applied by the authors. All third-party
* contributions are distributed under license by CARAVELATECH CONSULTORIA E
* TECNOLOGIA EM INFORMATICA LTDA.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
***********************************************************************
* OpenSpotLight - Plataforma de Governança de TI de Código Aberto
*
* Direitos Autorais Reservados (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA
* EM INFORMATICA LTDA ou como contribuidores terceiros indicados pela etiqueta
* @author ou por expressa atribuição de direito autoral declarada e atribuída pelo autor.
* Todas as contribuições de terceiros estão distribuídas sob licença da
* CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA.
*
* Este programa é software livre; você pode redistribuí-lo e/ou modificá-lo sob os
* termos da Licença Pública Geral Menor do GNU conforme publicada pela Free Software
* Foundation.
*
* Este programa é distribuído na expectativa de que seja útil, porém, SEM NENHUMA
* GARANTIA; nem mesmo a garantia implícita de COMERCIABILIDADE OU ADEQUAÇÃO A UMA
* FINALIDADE ESPECÍFICA. Consulte a Licença Pública Geral Menor do GNU para mais detalhes.
*
* Você deve ter recebido uma cópia da Licença Pública Geral Menor do GNU junto com este
* programa; se não, escreva para:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.openspotlight.remote.server;
import static java.text.MessageFormat.format;
import static org.openspotlight.common.util.Arrays.andOf;
import static org.openspotlight.common.util.Arrays.of;
import static org.openspotlight.common.util.Assertions.checkCondition;
import static org.openspotlight.common.util.Assertions.checkNotEmpty;
import static org.openspotlight.common.util.Assertions.checkNotNull;
import static org.openspotlight.common.util.Equals.eachEquality;
import static org.openspotlight.common.util.Exceptions.catchAndLog;
import static org.openspotlight.common.util.Exceptions.logAndReturn;
import static org.openspotlight.common.util.Exceptions.logAndReturnNew;
import static org.openspotlight.common.util.HashCodes.hashOf;
import gnu.cajo.invoke.Remote;
import gnu.cajo.utils.ItemServer;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.openspotlight.common.exception.ConfigurationException;
import org.openspotlight.common.util.Exceptions;
import org.openspotlight.common.util.Reflection;
import org.openspotlight.common.util.Reflection.UnwrappedCollectionTypeFromMethodReturn;
import org.openspotlight.common.util.Reflection.UnwrappedMapTypeFromMethodReturn;
import org.openspotlight.common.util.SLCollections;
import org.openspotlight.remote.annotation.DisposeMethod;
import org.openspotlight.remote.annotation.UnsupportedRemoteMethod;
import org.openspotlight.remote.internal.RemoteObjectInvocation;
import org.openspotlight.remote.internal.RemoteReference;
import org.openspotlight.remote.internal.RemoteReference.ObjectMethods;
import org.openspotlight.remote.internal.UserToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Class RemoteObjectServer will handle and take care of all object instances.
*/
public class RemoteObjectServerImpl implements RemoteObjectServer {
/**
* The Class ActivityMonitor.
*/
private class ActivityMonitor implements Runnable {
/*
* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
logger.trace("Starting GC...");
garbageCollection();
}
}
/**
* The Class RemoteReferenceInternalData.
*/
private static class RemoteReferenceInternalData<T> implements TimeoutAble, Comparable<RemoteReferenceInternalData<T>> {
/** The hashcode. */
private final int hashcode;
/** The last date access. */
private final AtomicLong lastDateAccess;
/** The object. */
private final T object;
/** The user token. */
private final RemoteReference<T> remoteReference;
final CopyOnWriteArraySet<RemoteReferenceInternalData<?>> children =
new CopyOnWriteArraySet<RemoteReferenceInternalData<?>>();
final RemoteReferenceInternalData<?> parentInternalData;
/**
* Instantiates a new remote reference internal data.
*
* @param remoteReference the remote reference
* @param object the object
* @param parentInternalData
*/
public RemoteReferenceInternalData(
final RemoteReference<T> remoteReference, final T object,
final RemoteReferenceInternalData<?> parentInternalData) {
super();
this.remoteReference = remoteReference;
this.parentInternalData = parentInternalData;
this.lastDateAccess = new AtomicLong(System.currentTimeMillis());
this.object = object;
this.hashcode = hashOf(this.remoteReference, this.lastDateAccess);
}
@Override
public int compareTo(final RemoteReferenceInternalData<T> o) {
final int thisSize = this.children.size();
final int anotherSize = o.children.size();
return thisSize < anotherSize ? -1 : (thisSize == anotherSize ? 0 : 1);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equalsTo(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (obj == this) { return true; }
if (!(obj instanceof RemoteReferenceInternalData<?>)) { return false; }
final RemoteReferenceInternalData<?> that = (RemoteReferenceInternalData<?>) obj;
return eachEquality(of(this.remoteReference, this.lastDateAccess), andOf(that.remoteReference, that.lastDateAccess));
}
/**
* Gets the last date access.
*
* @return the last date access
*/
@Override
public AtomicLong getLastDateAccess() {
return this.lastDateAccess;
}
/**
* Gets the object.
*
* @return the object
*/
public T getObject() {
return this.object;
}
/**
* Gets the remote reference.
*
* @return the remote reference
*/
public RemoteReference<T> getRemoteReference() {
return this.remoteReference;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return this.hashcode;
}
}
/**
* The Interface TimeoutAble.
*/
private interface TimeoutAble {
/**
* Gets the last date access.
*
* @return the last date access
*/
public AtomicLong getLastDateAccess();
}
/**
* The Class UserTokenInternalData.
*/
private static class UserTokenInternalData implements TimeoutAble {
/** The hashcode. */
private final int hashcode;
/** The last date access. */
private final AtomicLong lastDateAccess;
/** The user token. */
private final UserToken userToken;
/**
* Instantiates a new user token internal data.
*
* @param userToken the user token
*/
public UserTokenInternalData(
final UserToken userToken) {
super();
this.userToken = userToken;
lastDateAccess = new AtomicLong(System.currentTimeMillis());
hashcode = hashOf(this.userToken, lastDateAccess);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equalsTo(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (obj == this) { return true; }
if (!(obj instanceof UserTokenInternalData)) { return false; }
final UserTokenInternalData that = (UserTokenInternalData) obj;
return eachEquality(of(userToken, lastDateAccess), andOf(that.userToken, that.lastDateAccess));
}
/**
* Gets the last date access.
*
* @return the last date access
*/
@Override
public AtomicLong getLastDateAccess() {
return lastDateAccess;
}
/**
* Gets the user token.
*
* @return the user token
*/
public UserToken getUserToken() {
return userToken;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return hashcode;
}
}
private static RemoteObjectServerImpl defaultReference = null;
/** The remote references. */
private final Map<RemoteReference<?>, RemoteReferenceInternalData<?>> activeRemoteReferences =
new ConcurrentHashMap<RemoteReference<?>, RemoteReferenceInternalData<?>>();
/** The closed. */
private final AtomicBoolean closed = new AtomicBoolean(false);
/** The internal object factory map. */
private final Map<Class<?>, InternalObjectFactory<?>> internalObjectFactoryMap =
new HashMap<Class<?>, InternalObjectFactory<?>>();
private final Logger logger = LoggerFactory.getLogger(this
.getClass());
private final Integer portToUse;
/** The timeout in milliseconds. */
private final long timeoutInMilliseconds;
/** The user authenticator. */
private final UserAuthenticator userAuthenticator;
/** The last user access. */
private final Map<UserToken, UserTokenInternalData> userTokenDataMap =
new ConcurrentHashMap<UserToken, UserTokenInternalData>();
/**
* Instantiates a new remote object server impl.
*
* @param userAuthenticator the user autenticator
* @param portToUse the port to use
* @param timeoutInMilliseconds the timeout in milliseconds
*/
private RemoteObjectServerImpl(
final UserAuthenticator userAuthenticator, final Integer portToUse,
final Long timeoutInMilliseconds) {
try {
checkNotNull("userAutenticator", userAuthenticator);
checkNotNull("portToUse", portToUse);
checkCondition("portToUseBiggerThanZero", portToUse.intValue() > 0);
checkNotNull("timeoutInMinutes", timeoutInMilliseconds);
checkCondition("timeoutInMinutesBiggerThanOrEqualToZero", timeoutInMilliseconds.longValue() >= 0L);
this.userAuthenticator = userAuthenticator;
this.portToUse = portToUse;
this.timeoutInMilliseconds = timeoutInMilliseconds;
Remote.config(null, portToUse.intValue(), null, 0);
ItemServer.bind(this, "RemoteObjectServer");
new Thread(new ActivityMonitor(), "OSL-Remote-Server-GC").start();
} catch (final RemoteException e) {
throw logAndReturnNew(format("Problem starting remote object server inside port {0}", portToUse), e,
ConfigurationException.class);
}
}
public synchronized static RemoteObjectServer getDefault(final UserAuthenticator userAuthenticator,
final Integer portToUse,
final Long timeoutInMilliseconds) {
if (defaultReference == null) {
defaultReference = new RemoteObjectServerImpl(userAuthenticator, portToUse, timeoutInMilliseconds);
} else {
checkCondition("sameUserAutenticator", userAuthenticator.equals(defaultReference.userAuthenticator));
checkCondition("samePort", portToUse.equals(defaultReference.portToUse));
checkCondition("sameTimeoutInMilliseconds", timeoutInMilliseconds.equals(defaultReference.timeoutInMilliseconds));
}
return defaultReference;
}
/**
* Garbage collection.
*/
private void garbageCollection() {
while (true) {
try {
Thread.sleep(timeoutInMilliseconds / 2);
} catch (final InterruptedException e) {
}
final long curTimeMillis = System.currentTimeMillis();
final long acceptableDiff = curTimeMillis - timeoutInMilliseconds;
if (logger.isTraceEnabled()) {
logger.trace(format("diff in milliseconds {0} for timeout {1} and currentTimeMillis {2} ", acceptableDiff,
timeoutInMilliseconds, curTimeMillis));
}
final Set<UserTokenInternalData> deathUserEntries = new HashSet<UserTokenInternalData>();
final Set<Entry<UserToken, UserTokenInternalData>> userEntries = userTokenDataMap.entrySet();
for (final Entry<UserToken, UserTokenInternalData> entry: userEntries) {
if (entry.getValue().getLastDateAccess().get() < acceptableDiff) {
deathUserEntries.add(entry.getValue());
}
}
for (final UserTokenInternalData deathUserEntry: deathUserEntries) {
logger.info(format("removing user {0} token {1}", deathUserEntry.getUserToken().getUser(),
deathUserEntry.getUserToken().getToken()));
userTokenDataMap.remove(deathUserEntry.getUserToken());
final Set<Entry<RemoteReference<?>, RemoteReferenceInternalData<?>>> remoteObjectsEntries =
activeRemoteReferences.entrySet();
for (final Entry<RemoteReference<?>, RemoteReferenceInternalData<?>> entry: remoteObjectsEntries) {
final Set<RemoteReferenceInternalData<?>> deathEntries = new HashSet<RemoteReferenceInternalData<?>>();
if (entry.getKey().getUserToken().equals(deathUserEntry)) {
deathEntries.add(entry.getValue());
}
for (final RemoteReferenceInternalData<?> deathEntry: deathEntries) {
removeDeathEntry(deathEntry);
}
}
}
final Set<RemoteReferenceInternalData<?>> deathEntries = new HashSet<RemoteReferenceInternalData<?>>();
final Set<Entry<RemoteReference<?>, RemoteReferenceInternalData<?>>> remoteObjectsEntries =
activeRemoteReferences.entrySet();
for (final Entry<RemoteReference<?>, RemoteReferenceInternalData<?>> entry: remoteObjectsEntries) {
if (entry.getValue().getLastDateAccess().get() < acceptableDiff) {
deathEntries.add(entry.getValue());
}
}
for (final RemoteReferenceInternalData<?> deathEntry: deathEntries) {
removeDeathEntry(deathEntry);
}
if (closed.get()) {
logger.trace("Stopping GC...");
return;
}
}
}
/**
* Internal newPair remote reference.
*
* @param userToken the user token
* @param remoteReferenceType the remote reference type
* @param newObject the new object
* @return the remote reference< t>
*/
@SuppressWarnings("unchecked")
private <T> RemoteReference<T> internalCreateRemoteReference(final RemoteReferenceInternalData<?> parentInternalData,
final UserToken userToken,
final Class<T> remoteReferenceType,
final T newObject) {
if (newObject == null) { return null; }
RemoteReference<T> reference;
if (newObject != null) {
for (final Entry<RemoteReference<?>, RemoteReferenceInternalData<?>> entry: activeRemoteReferences.entrySet()) {
if (newObject == entry.getValue().getObject()) {
if (userToken.equals(entry.getKey().getUserToken())) {
reference = (RemoteReference<T>) entry.getKey();
return reference;
}
}
}
}
final String remoteReferenceId = UUID.randomUUID().toString();
reference = new RemoteReference<T>(remoteReferenceType, newObject.getClass().getInterfaces(), remoteReferenceId,
userToken);
final RemoteReferenceInternalData<T> internalData =
new RemoteReferenceInternalData<T>(reference, newObject, parentInternalData);
if (parentInternalData != null) {
parentInternalData.children.add(internalData);
}
activeRemoteReferences.put(reference, internalData);
updateRemoteReferenceIfNecessary(reference);
return reference;
}
/**
* Checks if is remote.
*
* @param method the method
* @return true, if is remote
* @throws Exception the exception
*/
private boolean isRemote(final Method method)
throws Exception {
final Class<?> returnType = method.getReturnType();
if (Collection.class.isAssignableFrom(returnType)) {
final UnwrappedCollectionTypeFromMethodReturn<Object> metadata = Reflection.unwrapCollectionFromMethodReturn(method);
return isTypeRemote(metadata.getItemType());
} else if (Map.class.isAssignableFrom(returnType)) {
final UnwrappedMapTypeFromMethodReturn<Object, Object> metadata = Reflection.unwrapMapFromMethodReturn(method);
return isTypeRemote(metadata.getItemType().getK2());
}
return isTypeRemote(returnType);
}
/**
* Checks if is remote reference valid.
*
* @param remoteReference the remote reference
* @return true, if is remote reference valid
*/
private boolean isRemoteReferenceValid(final RemoteReference<?> remoteReference)
throws RemoteReferenceInvalid, UserTokenInvalid {
checkNotNull("remoteReference", remoteReference);
checkCondition("userTokenValid", isUserTokenValid(remoteReference.getUserToken()));
final boolean contains = activeRemoteReferences.containsKey(remoteReference);
if (!contains) { throw logAndReturn(new RemoteReferenceInvalid(remoteReference.getRemoteType()
+ remoteReference.getRemoteReferenceId()
+ " is invalid. Try to get this object again.")); }
return true;
}
private boolean isTypeRemote(final Class<?> returnType) {
if (returnType == null) { return true; }
if (returnType.isPrimitive()) { return false; }
if (Serializable.class.isAssignableFrom(returnType)) { return false; }
return true;
}
/**
* Checks if is user token valid.
*
* @param userToken the user token
* @return true, if is user token valid
*/
private boolean isUserTokenValid(final UserToken userToken)
throws UserTokenInvalid {
checkNotNull("userToken", userToken);
final boolean contains = userTokenDataMap.containsKey(userToken);
if (!contains) { throw logAndReturn(new UserTokenInvalid(userToken.getUser() + " is invalid. Try to connect again.")); }
return true;
}
/**
* Removes the death entry.
*
* @param deathEntry the death entry
*/
private void removeDeathEntry(final RemoteReferenceInternalData<?> deathEntry) {
if (deathEntry == null) { return; }
for (final RemoteReferenceInternalData<?> toRemoveBefore: deathEntry.children) {
removeDeathEntry(toRemoveBefore);
}
activeRemoteReferences.remove(deathEntry.getRemoteReference());
final Method[] methods = deathEntry.getObject().getClass().getMethods();
for (final Method m: methods) {
if (m.isAnnotationPresent(DisposeMethod.class) && m.getParameterTypes().length == 0) {
final DisposeMethod disposeAnnotation = m.getAnnotation(DisposeMethod.class);
if (disposeAnnotation.callOnTimeout()) {
try {
m.invoke(deathEntry.getObject());
return;
} catch (final Exception e) {
catchAndLog(e);
}
}
}
}
}
/**
* Update remote reference.
*
* @param remoteReference the remote reference
*/
private void updateRemoteReferenceIfNecessary(final RemoteReference<?> remoteReference) {
checkNotNull("remoteReference", remoteReference);
if (activeRemoteReferences.containsKey(remoteReference)) {
activeRemoteReferences.get(remoteReference).getLastDateAccess().set(System.currentTimeMillis());
updateUserToken(remoteReference.getUserToken());
}
}
/**
* Update user token.
*
* @param userToken the user token
*/
private void updateUserToken(final UserToken userToken) {
checkNotNull("userToken", userToken);
checkCondition("userTokenValid", userTokenDataMap.containsKey(userToken));
userTokenDataMap.get(userToken).getLastDateAccess().set(System.currentTimeMillis());
}
@Override
public synchronized void closeAllObjects() {
for (final Entry<RemoteReference<?>, RemoteReferenceInternalData<?>> e: activeRemoteReferences.entrySet()) {
try {
removeDeathEntry(e.getValue());
} catch (final Exception ex) {
Exceptions.catchAndLog("error on closing death object entry", ex);
}
}
}
/*
* (non-Javadoc)
* @see org.openspotlight.remote.server.RemoteObjectServer#createRemoteReference (org.openspotlight.remote.internal.UserToken,
* java.lang.Class, java.lang.Object[])
*/
@Override
@SuppressWarnings("unchecked")
public <T> RemoteReference<T> createRemoteReference(final UserToken userToken,
final Class<T> remoteReferenceType,
final Object... parameters)
throws InvalidReferenceTypeException {
try {
checkNotNull("userToken", userToken);
checkNotNull("remoteReferenceType", remoteReferenceType);
checkCondition("remoteReferenceTypeIsInterface", remoteReferenceType.isInterface());
checkCondition("remoteReferenceTypeContainsFactory", internalObjectFactoryMap.containsKey(remoteReferenceType));
checkCondition("isUserTokenValid", isUserTokenValid(userToken));
final InternalObjectFactory<T> internalFactory =
(InternalObjectFactory<T>) internalObjectFactoryMap.get(remoteReferenceType);
final T newObject = internalFactory.createNewInstance(parameters);
final RemoteReference<T> reference =
this.internalCreateRemoteReference(null, userToken, remoteReferenceType, newObject);
return reference;
} catch (final Exception e) {
throw logAndReturnNew(e, InvalidReferenceTypeException.class);
} finally {
updateUserToken(userToken);
}
}
/**
* Creates the user token.
*
* @param user the user
* @param password the password
* @param clientHost the client host
* @return the user token
* @throws AccessDeniedException the access denied exception
*/
@Override
public UserToken createUserToken(final String user,
final String password,
final String clientHost)
throws AccessDeniedException {
checkNotEmpty("user", user);
checkNotEmpty("password", password);
checkNotEmpty("clientHost", clientHost);
final boolean canConnect = userAuthenticator.canConnect(user, password, clientHost);
if (!canConnect) { throw logAndReturn(new AccessDeniedException(format(
"User {0} from host {1} can't connect to this server instance",
user, clientHost))); }
final UserToken token = new UserToken(user, UUID.randomUUID().toString());
userTokenDataMap.put(token, new UserTokenInternalData(token));
// FIXME see if we need some limit, for example: if an user connect
// again, invalidate the last token
return token;
}
/*
* (non-Javadoc)
* @see org.openspotlight.remote.server.RemoteObjectServer#invokeRemoteMethod
* (org.openspotlight.remote.internal.RemoteObjectInvocation)
*/
@Override
@SuppressWarnings("unchecked")
public <T, R> AbstractInvocationResponse<R> invokeRemoteMethod(final RemoteObjectInvocation<T> invocation)
throws InternalErrorOnMethodInvocationException, InvocationTargetException, RemoteReferenceInvalid, UserTokenInvalid {
checkNotNull("invocation", invocation);
checkCondition("remoteReferenceValid:" + invocation.getMethodName(),
isRemoteReferenceValid(invocation.getRemoteReference()));
try {
final RemoteReferenceInternalData<T> remoteReferenceData =
(RemoteReferenceInternalData<T>) activeRemoteReferences.get(invocation.getRemoteReference());
final T object = remoteReferenceData.getObject();
Method method = null;
for (final Class<?> iface: invocation.getRemoteReference().getInterfaces()) {
if (iface.equals(ObjectMethods.class)) {
continue;
}
try {
method = iface.getMethod(invocation.getMethodName(), invocation.getParameterTypes());
break;
} catch (final NoSuchMethodException e) {
}
}
if (method == null) {
try {
method = Object.class.getMethod(invocation.getMethodName(), invocation.getParameterTypes());
} catch (final NoSuchMethodException e) {
}
}
checkCondition("methodNotNull:" + invocation.getMethodName(), method != null);
if (method.isAnnotationPresent(UnsupportedRemoteMethod.class)) { throw new UnsupportedOperationException(); }
final Object[] args = invocation.getParameters();
for (int i = 0, size = args.length; i < size; i++) {
if (args[i] instanceof RemoteReference<?>) {
final RemoteReference<?> ref = (RemoteReference<?>) args[i];
args[i] = activeRemoteReferences.get(ref).getObject();
} else if (args[i] instanceof Collection<?>) {
final Collection<Object> collection = (Collection<Object>) args[i];
final Iterator<?> it = collection.iterator();
Object o = null;
while (o == null) {
o = it.next();
}
if (o instanceof RemoteReference<?>) {
// here it needs to use the correct reference instead of
// using the remote one
final Collection<Object> newCollection = SLCollections.createNewCollection(collection.getClass(),
collection.size());
for (final Object item: collection) {
if (item instanceof RemoteReference<?>) {
final RemoteReference<Object> remoteRef = (RemoteReference<Object>) item;
final Object correctInstance = activeRemoteReferences.get(remoteRef).getObject();
newCollection.add(correctInstance);
} else {
newCollection.add(item);
}
}
args[i] = newCollection;
}
} else if (args[i] instanceof Map<?, ?>) {
final Map<Object, Object> map = (Map<Object, Object>) args[i];
final Iterator<Entry<Object, Object>> it = map.entrySet().iterator();
Object o = null;
while (o == null) {
o = it.next().getValue();
}
if (o instanceof RemoteReference<?>) {
// here it needs to use the correct reference instead of
// using the remote one
final Map<Object, Object> newMap = new HashMap<Object, Object>();
for (final Entry<Object, Object> entry: map.entrySet()) {
if (entry.getValue() instanceof RemoteReference<?>) {
final RemoteReference<Object> remoteRef = (RemoteReference<Object>) entry.getValue();
final Object correctInstance = activeRemoteReferences.get(remoteRef).getObject();
newMap.put(entry.getKey(), correctInstance);
} else {
newMap.put(entry.getKey(), entry.getValue());
}
}
args[i] = newMap;
}
}
}
final R result = (R) method.invoke(object, invocation.getParameters());
if (method.isAnnotationPresent(DisposeMethod.class)) {
removeDeathEntry(remoteReferenceData);
}
if (isRemote(method)) {
if (result instanceof Collection) {
final AbstractInvocationResponse<R> response =
(AbstractInvocationResponse<R>) this
.wrapResultIntoCollection(
remoteReferenceData,
method,
invocation
.getUserToken(),
(Collection<?>) result);
return response;
}
if (result instanceof Map) {
final AbstractInvocationResponse<R> response =
(AbstractInvocationResponse<R>) this
.wrapResultIntoMap(
remoteReferenceData,
method,
invocation
.getUserToken(),
(Map<?, ?>) result);
return response;
}
final RemoteReference<R> remoteReference =
this.internalCreateRemoteReference(remoteReferenceData,
invocation.getUserToken(),
(Class<R>) invocation
.getReturnType(),
result);
return new RemoteReferenceInvocationResponse(remoteReference);
}
return new LocalCopyInvocationResponse(result);
} catch (final UnsupportedOperationException e) {
throw logAndReturn(e);
} catch (final InvocationTargetException e) {
throw logAndReturn(e);
} catch (final Exception e) {
throw logAndReturnNew(e, InternalErrorOnMethodInvocationException.class);
} finally {
updateRemoteReferenceIfNecessary(invocation.getRemoteReference());
}
}
/*
* (non-Javadoc)
* @seeorg.openspotlight.remote.server.RemoteObjectServer# registerInternalObjectFactory(java.lang.Class,
* org.openspotlight.remote.server.RemoteObjectServer.InternalObjectFactory)
*/
@Override
public <T> void registerInternalObjectFactory(final Class<T> objectType,
final InternalObjectFactory<T> factory) {
checkNotNull("objectType", objectType);
checkNotNull("factory", factory);
internalObjectFactoryMap.put(objectType, factory);
}
/**
* Shutdown.
*/
@Override
public synchronized void shutdown() {
Remote.shutdown();
closed.set(true);
for (final Entry<Class<?>, InternalObjectFactory<?>> entry: internalObjectFactoryMap.entrySet()) {
entry.getValue().shutdown();
}
closeAllObjects();
}
@SuppressWarnings("unchecked")
public <W, R extends Collection<W>> AbstractInvocationResponse<R>
wrapResultIntoCollection(final RemoteReferenceInternalData<?> parentInternalData,
final Method method,
final UserToken userToken,
final R collection)
throws Exception {
final UnwrappedCollectionTypeFromMethodReturn<Object> metadata = Reflection.unwrapCollectionFromMethodReturn(method);
final Class<? extends Iterable<?>> collectionType = metadata.getCollectionType();
final Class<W> remoteReferenceType = (Class<W>) metadata.getItemType();
final Collection<RemoteReference<W>> remoteReferencesCollection = SLCollections.createNewCollection(collectionType, 0);
for (final W o: collection) {
final RemoteReference<W> remoteRef =
this.internalCreateRemoteReference(parentInternalData, userToken, remoteReferenceType, o);
remoteReferencesCollection.add(remoteRef);
}
final CollectionOfRemoteInvocationResponse<W, R> wrapped =
new CollectionOfRemoteInvocationResponse<W, R>(
(Class<R>) collectionType,
remoteReferencesCollection);
return wrapped;
}
@SuppressWarnings("unchecked")
public <K, W, R extends Map<K, W>> AbstractInvocationResponse<R>
wrapResultIntoMap(final RemoteReferenceInternalData<?> parentInternalData,
final Method method,
final UserToken userToken,
final R map)
throws Exception {
final UnwrappedMapTypeFromMethodReturn<Object, Object> metadata = Reflection.unwrapMapFromMethodReturn(method);
final Class<W> remoteReferenceType = (Class<W>) metadata.getItemType().getK2();
final Map<K, RemoteReference<W>> remoteReferencesMap = new HashMap<K, RemoteReference<W>>(map.size());
for (final Entry<K, W> o: map.entrySet()) {
final RemoteReference<W> remoteRef =
this.internalCreateRemoteReference(parentInternalData, userToken, remoteReferenceType, o.getValue());
remoteReferencesMap.put(o.getKey(), remoteRef);
}
final MapOfRemoteInvocationResponse<K, W, R> wrapped = new MapOfRemoteInvocationResponse<K, W, R>(remoteReferencesMap);
return wrapped;
}
}