diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponent.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponent.java index 8eb9ac7b65cf369e099f9ffbd3e1a36afc0f9530..46b3d0e70c101e5ea9c8989b72b409a3f41ce9c4 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponent.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponent.java @@ -22,6 +22,7 @@ import java.util.List; import org.springframework.remoting.RemoteAccessException; import org.springframework.remoting.RemoteConnectFailureException; +import ch.systemsx.cisd.common.api.IRpcServiceFactory; import ch.systemsx.cisd.common.api.RpcServiceInterfaceDTO; import ch.systemsx.cisd.common.api.RpcServiceInterfaceVersionDTO; import ch.systemsx.cisd.common.exceptions.AuthorizationFailureException; @@ -33,8 +34,6 @@ import ch.systemsx.cisd.openbis.dss.api.v1.client.IDataSetDss; import ch.systemsx.cisd.openbis.dss.api.v1.client.IDssComponent; import ch.systemsx.cisd.openbis.dss.api.v1.shared.FileInfoDssDTO; import ch.systemsx.cisd.openbis.dss.api.v1.shared.IDssServiceRpcGeneric; -import ch.systemsx.cisd.openbis.dss.rpc.client.DssServiceRpcFactory; -import ch.systemsx.cisd.openbis.dss.rpc.client.IDssServiceRpcFactory; import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStore; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData; @@ -55,7 +54,7 @@ public class DssComponent implements IDssComponent private final IETLLIMSService openBisService; - private final IDssServiceRpcFactory dssServiceFactory; + private final IRpcServiceFactory dssServiceFactory; private AbstractDssComponentState state; @@ -117,7 +116,7 @@ public class DssComponent implements IDssComponent * @param sessionTokenOrNull A session token, if the user has already logged in, or null * otherwise. */ - protected DssComponent(IETLLIMSService service, IDssServiceRpcFactory dssServiceFactory, + protected DssComponent(IETLLIMSService service, IRpcServiceFactory dssServiceFactory, String sessionTokenOrNull) { this.openBisService = service; @@ -267,12 +266,12 @@ class AuthenticatedState extends AbstractDssComponentState { private final String sessionToken; - private final IDssServiceRpcFactory dssServiceFactory; + private final IRpcServiceFactory dssServiceFactory; /** * @param service */ - AuthenticatedState(IETLLIMSService service, IDssServiceRpcFactory dssServiceFactory, + AuthenticatedState(IETLLIMSService service, IRpcServiceFactory dssServiceFactory, String sessionToken) { super(service); diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssServiceRpcFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssServiceRpcFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..9f5a993a86c0c55b6112f3873410096824a1eb97 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssServiceRpcFactory.java @@ -0,0 +1,176 @@ +/* + * Copyright 2010 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.api.v1.client.impl; + +import java.io.File; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.List; + +import ch.systemsx.cisd.common.api.IRpcService; +import ch.systemsx.cisd.common.api.IRpcServiceFactory; +import ch.systemsx.cisd.common.api.IRpcServiceNameServer; +import ch.systemsx.cisd.common.api.IncompatibleAPIVersionsException; +import ch.systemsx.cisd.common.api.RpcServiceInterfaceDTO; +import ch.systemsx.cisd.common.api.RpcServiceInterfaceVersionDTO; +import ch.systemsx.cisd.common.spring.HttpInvokerUtils; +import ch.systemsx.cisd.common.ssl.SslCertificateHelper; + +/** + * Client-side factory for DssServiceRpc objects. + * <p> + * Create client-side proxies to server RPC interface objects. + * + * @author Chandrasekhar Ramakrishnan + */ +// TODO: This code should be refactored and moved into a common location, but it is a bit difficult +// due to dependencies. +public class DssServiceRpcFactory implements IRpcServiceFactory +{ + private static final int SERVER_TIMEOUT_MIN = 5; + + private static final String NAME_SERVER_SUFFIX = "/rpc"; + + public List<RpcServiceInterfaceDTO> getSupportedInterfaces(String serverURL, + boolean getServerCertificateFromServer) throws IncompatibleAPIVersionsException + { + // We assume the location of the name server follows the convention + String nameServerURL = serverURL + NAME_SERVER_SUFFIX; + Class<IRpcServiceNameServer> clazz = IRpcServiceNameServer.class; + if (getServerCertificateFromServer) + { + new SslCertificateHelper(nameServerURL, getConfigDirectory(), "dss").setUpKeyStore(); + } + + IRpcServiceNameServer nameServer = + new ServiceProxyBuilder<IRpcServiceNameServer>(nameServerURL, clazz, + SERVER_TIMEOUT_MIN, 1).getServiceInterface(); + return nameServer.getSupportedInterfaces(); + } + + public <T extends IRpcService> T getService(RpcServiceInterfaceVersionDTO ifaceVersion, + Class<T> ifaceClazz, String serverURL, boolean getServerCertificateFromServer) + throws IncompatibleAPIVersionsException + { + String serviceURL = serverURL + ifaceVersion.getUrlSuffix(); + if (getServerCertificateFromServer) + { + new SslCertificateHelper(serviceURL, getConfigDirectory(), "dss").setUpKeyStore(); + } + + return new ServiceProxyBuilder<T>(serviceURL, ifaceClazz, SERVER_TIMEOUT_MIN, 1) + .getServiceInterface(); + } + + private File getConfigDirectory() + { + String homeDir = System.getProperty("dss.root"); + File configDir; + if (homeDir != null) + { + configDir = new File(homeDir, "etc"); + } else + { + homeDir = System.getProperty("user.home"); + configDir = new File(homeDir, ".dss"); + } + configDir.mkdirs(); + return configDir; + } +} + +/** + * Internal helper class for constructing service proxies; + * + * @author Chandrasekhar Ramakrishnan + */ +@SuppressWarnings("unchecked") +class ServiceProxyBuilder<T extends IRpcService> +{ + private final String serviceURL; + + private final Class<?> clazz; + + private final int serverTimeoutMin; + + private final int apiClientVersion; + + ServiceProxyBuilder(String serviceURL, Class<?> clazz, int serverTimeoutMin, + int apiClientVersion) + { + this.serviceURL = serviceURL; + this.clazz = clazz; + this.serverTimeoutMin = serverTimeoutMin; + this.apiClientVersion = apiClientVersion; + } + + T getServiceInterface() throws IncompatibleAPIVersionsException + { + T service = getRawServiceProxy(); + service = wrapProxyInServiceInvocationHandler(service); + final int apiServerVersion = service.getMajorVersion(); + if (apiClientVersion != apiServerVersion) + { + throw new IncompatibleAPIVersionsException(apiClientVersion, apiServerVersion); + } + + return service; + } + + private T getRawServiceProxy() + { + return (T) HttpInvokerUtils.createStreamSupportingServiceStub(clazz, serviceURL, + serverTimeoutMin); + } + + private T wrapProxyInServiceInvocationHandler(T service) + { + final ClassLoader classLoader = DssServiceRpcFactory.class.getClassLoader(); + final ServiceInvocationHandler invocationHandler = new ServiceInvocationHandler(service); + final T proxy = (T) Proxy.newProxyInstance(classLoader, new Class[] + { clazz }, invocationHandler); + return proxy; + } + + /** + * An invocation handler that unwraps exceptions that occur in methods called via reflection. + * + * @author Chandrasekhar Ramakrishnan + */ + private static final class ServiceInvocationHandler implements InvocationHandler + { + private final IRpcService service; + + private ServiceInvocationHandler(IRpcService service) + { + this.service = service; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + try + { + return method.invoke(service, args); + } catch (InvocationTargetException ex) + { + throw ex.getCause(); + } + } + } +} diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponentTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponentTest.java index 7050d6ea99843c1a4c721ec7d46b004b5b84f49a..02b2e10d963afb0328f8f03987a15989063eed9c 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponentTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponentTest.java @@ -31,6 +31,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase; +import ch.systemsx.cisd.common.api.IRpcServiceFactory; import ch.systemsx.cisd.common.api.RpcServiceInterfaceDTO; import ch.systemsx.cisd.common.api.RpcServiceInterfaceVersionDTO; import ch.systemsx.cisd.openbis.dss.api.v1.client.IDataSetDss; @@ -38,7 +39,6 @@ import ch.systemsx.cisd.openbis.dss.api.v1.client.impl.DssComponent; import ch.systemsx.cisd.openbis.dss.api.v1.shared.FileInfoDssBuilder; import ch.systemsx.cisd.openbis.dss.api.v1.shared.FileInfoDssDTO; import ch.systemsx.cisd.openbis.dss.api.v1.shared.IDssServiceRpcGeneric; -import ch.systemsx.cisd.openbis.dss.rpc.client.IDssServiceRpcFactory; import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStore; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData; @@ -53,7 +53,7 @@ public class DssComponentTest extends AbstractFileSystemTestCase private IETLLIMSService openBisService; - private IDssServiceRpcFactory dssServiceFactory; + private IRpcServiceFactory dssServiceFactory; private DssComponent dssComponent; @@ -75,7 +75,7 @@ public class DssComponentTest extends AbstractFileSystemTestCase super.setUp(); context = new Mockery(); openBisService = context.mock(IETLLIMSService.class); - dssServiceFactory = context.mock(IDssServiceRpcFactory.class); + dssServiceFactory = context.mock(IRpcServiceFactory.class); dssComponent = new DssComponent(openBisService, dssServiceFactory, null); randomDataFile = getFileWithRandomData(1); } diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponentTestClient.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponentTestClient.java index efc710bfd463df75ce17155d9d3ba9546219475f..297d8fdb5aebb322253b038bc8f76af8f0ea19d9 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponentTestClient.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/api/v1/client/impl/DssComponentTestClient.java @@ -21,7 +21,9 @@ import java.util.Properties; import org.apache.log4j.PropertyConfigurator; import ch.systemsx.cisd.openbis.dss.api.v1.client.DssComponentFactory; +import ch.systemsx.cisd.openbis.dss.api.v1.client.IDataSetDss; import ch.systemsx.cisd.openbis.dss.api.v1.client.IDssComponent; +import ch.systemsx.cisd.openbis.dss.api.v1.shared.FileInfoDssDTO; /** * @author Chandrasekhar Ramakrishnan @@ -35,7 +37,12 @@ public class DssComponentTestClient System.out.println("Logging in"); IDssComponent component = DssComponentFactory.tryCreate("test", "foobar", "http://localhost:8888/openbis"); - component.getDataSet("20100318094819344-4"); + IDataSetDss dataSet = component.getDataSet("20100318094819344-4"); + FileInfoDssDTO fileInfos[] = dataSet.listFiles("/", true); + for (FileInfoDssDTO fileInfo : fileInfos) + { + System.out.println(fileInfo); + } component.logout(); System.out.println("Logging out"); }