/**
* 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.client;
import static java.text.MessageFormat.format;
import static org.openspotlight.common.util.Assertions.checkNotNull;
import static org.openspotlight.common.util.Exceptions.logAndReturn;
import static org.openspotlight.common.util.Exceptions.logAndReturnNew;
import gnu.cajo.utils.extra.TransparentItemProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.openspotlight.common.util.SLCollections;
import org.openspotlight.common.util.reflection.MethodIdentificationSupport;
import org.openspotlight.common.util.reflection.MethodIdentificationSupport.MethodWithParametersKey;
import org.openspotlight.remote.annotation.CachedInvocation;
import org.openspotlight.remote.annotation.UnsupportedRemoteMethod;
import org.openspotlight.remote.internal.RemoteObjectInvocation;
import org.openspotlight.remote.internal.RemoteReference;
import org.openspotlight.remote.internal.UserToken;
import org.openspotlight.remote.server.AccessDeniedException;
import org.openspotlight.remote.server.InvalidReferenceTypeException;
import org.openspotlight.remote.server.RemoteObjectServer;
import org.openspotlight.remote.server.RemoteObjectServer.AbstractInvocationResponse;
import org.openspotlight.remote.server.RemoteObjectServer.CollectionOfRemoteInvocationResponse;
import org.openspotlight.remote.server.RemoteObjectServer.LocalCopyInvocationResponse;
import org.openspotlight.remote.server.RemoteObjectServer.MapOfRemoteInvocationResponse;
import org.openspotlight.remote.server.RemoteObjectServer.RemoteReferenceInvocationResponse;
/**
* A factory for creating RemoteObject objects.
*/
public class RemoteObjectFactory {
/**
* The Class RemoteReferenceHandler.
*/
private static class RemoteReferenceHandler<T> implements InvocationHandler {
/**
* The Class ExceptionWrapper.
*/
private static class ExceptionWrapper {
/** The throwable. */
private final Throwable throwable;
/**
* Instantiates a new exception wrapper.
*
* @param throwable the throwable
*/
public ExceptionWrapper(
final Throwable throwable) {
super();
this.throwable = throwable;
}
/**
* Gets the throwable.
*
* @return the throwable
*/
public Throwable getThrowable() {
return this.throwable;
}
}
/** The Constant EMPTY_ARR. */
private static final Object[] EMPTY_ARR = new Object[0];
/** The Constant NULL_VALUE. */
private static final Object NULL_VALUE = new Object();
/** The from server. */
private final RemoteObjectServer fromServer;
/** The method result cache. */
private final Map<MethodWithParametersKey, Object> methodResultCache =
new ConcurrentHashMap<MethodWithParametersKey, Object>();
/** The remote reference. */
private final RemoteReference<T> remoteReference;
/**
* Instantiates a new remote reference handler.
*
* @param fromServer the from server
* @param remoteReference the remote reference
*/
public RemoteReferenceHandler(
final RemoteObjectServer fromServer, final RemoteReference<T> remoteReference) {
checkNotNull("fromServer", fromServer);
checkNotNull("remoteReference", remoteReference);
this.remoteReference = remoteReference;
this.fromServer = fromServer;
}
/**
* Return result from cache.
*
* @param key the key
* @return the object
* @throws Throwable the throwable
*/
private Object returnResultFromCache(final MethodWithParametersKey key)
throws Throwable {
final Object value = this.methodResultCache.get(key);
if (value == NULL_VALUE) { return null; }
if (value instanceof ExceptionWrapper) {
final ExceptionWrapper ex = (ExceptionWrapper) value;
throw ex.getThrowable();
}
return value;
}
/**
* Store result on cache.
*
* @param key the key
* @param resultFromMethod the result from method
*/
private void storeResultOnCache(final MethodWithParametersKey key,
final Object resultFromMethod) {
if (resultFromMethod == null) {
this.methodResultCache.put(key, NULL_VALUE);
} else {
this.methodResultCache.put(key, resultFromMethod);
}
}
/**
* Gets the remote reference.
*
* @return the remote reference
*/
public RemoteReference<T> getRemoteReference() {
return this.remoteReference;
}
/*
* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
@SuppressWarnings("unchecked")
public Object invoke(final Object proxy,
final Method method,
final Object[] args)
throws Throwable {
if (method.isAnnotationPresent(UnsupportedRemoteMethod.class)) { throw new UnsupportedOperationException(); }
final Class<?>[] parameterTypes = method.getParameterTypes();
MethodWithParametersKey key = null;
if (method.isAnnotationPresent(CachedInvocation.class)) {
final String uniqueName = MethodIdentificationSupport.getMethodUniqueName(method);
key = new MethodWithParametersKey(uniqueName, args == null ? EMPTY_ARR : args);
if (this.methodResultCache.containsKey(key)) { return this.returnResultFromCache(key); }
}
if (args != null) {
for (int i = 0, size = args.length; i < size; i++) {
if (args[i] != null) {
if (Proxy.isProxyClass(args[i].getClass())) {
final InvocationHandler invocationHandler = Proxy.getInvocationHandler(args[i]);
if (invocationHandler instanceof RemoteReferenceHandler<?>) {
final RemoteReferenceHandler<?> handler = (RemoteReferenceHandler<?>) invocationHandler;
args[i] = handler.getRemoteReference();
}
} else if (args[i] instanceof Collection) {
final Collection<?> collection = (Collection<?>) args[i];
if (collection.size() > 0) {
final Iterator<?> it = collection.iterator();
Object o = null;
while (o == null) {
o = it.next();
}
if (Proxy.isProxyClass(o.getClass())) {
final InvocationHandler invocationHandlerForTest = Proxy.getInvocationHandler(o);
if (invocationHandlerForTest instanceof RemoteReferenceHandler<?>) {
// here, it *needs* to wrap only the
// references before sending it to the
// server
final Collection<Object> newCollection =
SLCollections
.createNewCollection(
collection
.getClass(),
collection
.size());
for (final Object item: collection) {
if (item != null) {
final InvocationHandler invocationHandler = Proxy.getInvocationHandler(item);
if (invocationHandler instanceof RemoteReferenceHandler<?>) {
final RemoteReferenceHandler<?> handler =
(RemoteReferenceHandler<?>) invocationHandler;
final Object newO = handler.getRemoteReference();
newCollection.add(newO);
} else {
newCollection.add(item);
}
} else {
newCollection.add(null);
}
}
args[i] = newCollection;
}
}
}
} else if (args[i] instanceof Map) {
final Map<Object, Object> map = (Map<Object, Object>) args[i];
if (map.size() > 0) {
final Iterator<Entry<Object, Object>> it = map.entrySet().iterator();
Object o = null;
while (o == null) {
o = it.next().getValue();
}
if (Proxy.isProxyClass(o.getClass())) {
final InvocationHandler invocationHandlerForTest = Proxy.getInvocationHandler(o);
if (invocationHandlerForTest instanceof RemoteReferenceHandler<?>) {
// here, it *needs* to wrap only the
// references before sending it to the
// server
final Map<Object, Object> newMap = new HashMap<Object, Object>();
for (final Entry<Object, Object> item: map.entrySet()) {
if (item.getValue() != null) {
final InvocationHandler invocationHandler =
Proxy.getInvocationHandler(item.getValue());
if (invocationHandler instanceof RemoteReferenceHandler<?>) {
final RemoteReferenceHandler<?> handler =
(RemoteReferenceHandler<?>) invocationHandler;
final Object newO = handler.getRemoteReference();
newMap.put(item.getKey(), newO);
} else {
newMap.put(item.getKey(), item.getValue());
}
} else {
newMap.put(item.getKey(), null);
}
}
args[i] = newMap;
}
}
}
}
}
}
}
final RemoteObjectInvocation<T> invocation = new RemoteObjectInvocation<T>(method.getReturnType(), parameterTypes,
args == null ? EMPTY_ARR : args,
method.getName(), this.remoteReference);
try {
Object resultFromMethod = null;
final AbstractInvocationResponse<Object> result = this.fromServer.invokeRemoteMethod(invocation);
if (result instanceof LocalCopyInvocationResponse<?>) {
final LocalCopyInvocationResponse<Object> localCopy = (LocalCopyInvocationResponse<Object>) result;
resultFromMethod = localCopy.getLocalCopy();
} else if (result instanceof RemoteReferenceInvocationResponse<?>) {
final RemoteReferenceInvocationResponse<Object> remoteReferenceResponse =
(RemoteReferenceInvocationResponse<Object>) result;
final RemoteReference<Object> methodResponseRemoteReference = remoteReferenceResponse.getRemoteReference();
if (methodResponseRemoteReference == null) {
resultFromMethod = null;
} else {
resultFromMethod =
Proxy
.newProxyInstance(
this.getClass().getClassLoader(),
methodResponseRemoteReference.getInterfaces(),
new RemoteReferenceHandler<Object>(
this.fromServer,
methodResponseRemoteReference));
}
} else if (result instanceof CollectionOfRemoteInvocationResponse) {
final CollectionOfRemoteInvocationResponse resultCollection = (CollectionOfRemoteInvocationResponse) result;
final Collection<Object> remoteResultCollection =
SLCollections
.createNewCollection(
resultCollection
.getResultType(),
resultCollection.getResult().size());
final Collection<RemoteReference<Object>> colection = resultCollection.getResult();
for (final RemoteReference<Object> remoteRef: colection) {
if (remoteRef != null) {
final Object proxyInstance =
Proxy
.newProxyInstance(
this.getClass().getClassLoader(),
remoteRef.getInterfaces(),
new RemoteReferenceHandler<Object>(
this.fromServer,
remoteRef));
remoteResultCollection.add(proxyInstance);
} else {
remoteResultCollection.add(null);
}
}
resultFromMethod = remoteResultCollection;
} else if (result instanceof MapOfRemoteInvocationResponse) {
final MapOfRemoteInvocationResponse resultMap = (MapOfRemoteInvocationResponse) result;
final Map<Object, Object> remoteResultMap = new HashMap<Object, Object>();
final Set<Entry<Object, Object>> entrySet = resultMap.getResult().entrySet();
for (final Entry<Object, Object> remoteRef: entrySet) {
final RemoteReference<?> remoteRefValue = (RemoteReference<?>) remoteRef.getValue();
if (remoteRefValue != null) {
final Object proxyInstance =
Proxy
.newProxyInstance(
this.getClass().getClassLoader(),
remoteRefValue.getInterfaces(),
new RemoteReferenceHandler<Object>(
this.fromServer,
(RemoteReference<Object>) remoteRef
.getValue()));
remoteResultMap.put(remoteRef.getKey(), proxyInstance);
} else {
remoteResultMap.put(remoteRef.getKey(), null);
}
}
resultFromMethod = remoteResultMap;
} else {
throw logAndReturn(new IllegalStateException());
}
if (method.isAnnotationPresent(CachedInvocation.class)) {
this.storeResultOnCache(key, resultFromMethod);
}
return resultFromMethod;
} catch (final InvocationTargetException e) {
if (method.isAnnotationPresent(CachedInvocation.class)) {
this.storeResultOnCache(key, new ExceptionWrapper(e.getCause()));
}
throw e.getCause();
} catch (final Exception e) {
throw logAndReturn(e);
}
}
}
/** The from server. */
final RemoteObjectServer fromServer;
/** The user token. */
final UserToken userToken;
/**
* Instantiates a new remote object factory.
*
* @param host the host
* @param port the port
* @param userName the user name
* @param password the password
* @throws CantConnectException the cant connect exception
* @throws AccessDeniedException the access denied exception
*/
public RemoteObjectFactory(
final String host, final int port, final String userName, final String password)
throws CantConnectException, AccessDeniedException {
String clientHost;
try {
clientHost = InetAddress.getLocalHost().getHostAddress().toString();
final String connectionString = format("//{0}:{1}/RemoteObjectServer", host, new Integer(port).toString());
fromServer = (RemoteObjectServer) TransparentItemProxy.getItem(connectionString,
new Class<?>[] {RemoteObjectServer.class});
} catch (final Exception e) {
throw logAndReturnNew(e, CantConnectException.class);
}
userToken = fromServer.createUserToken(userName, password, clientHost);
}
/**
* Creates a new object on server.
*
* @param remoteObjectType the remote object type
* @param parameters the parameters
* @return the T
* @throws InvalidReferenceTypeException the invalid reference type exception
*/
@SuppressWarnings("unchecked")
public <T> T createRemoteObject(final Class<T> remoteObjectType,
final Object... parameters)
throws InvalidReferenceTypeException {
final RemoteReference<T> remoteReference = fromServer.createRemoteReference(userToken, remoteObjectType,
parameters);
final T newObjectProxy = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), remoteReference.getInterfaces(),
new RemoteReferenceHandler(fromServer, remoteReference));
return newObjectProxy;
}
}