diff --git a/datastore_server/.classpath b/datastore_server/.classpath index 29b177fb65e4d460ca3f55accc147e86ddbc86bd..56d7ddafa9547c814ebbf67e91c90ed58c375fe7 100644 --- a/datastore_server/.classpath +++ b/datastore_server/.classpath @@ -72,5 +72,8 @@ <classpathentry kind="lib" path="/libraries/mina/mina-core.jar"/> <classpathentry kind="lib" path="/libraries/jython/jython.jar" sourcepath="/libraries/jython/jython_src.zip"/> <classpathentry kind="lib" path="/libraries/pngj/pngj.jar" sourcepath="/libraries/pngj/pngj_src.zip"/> + <classpathentry kind="lib" path="/libraries/jackson/jackson-core-asl.jar"/> + <classpathentry kind="lib" path="/libraries/jackson/jackson-mapper-asl.jar"/> + <classpathentry kind="lib" path="/libraries/jsonrpc4j/jsonrpc4j.jar" sourcepath="/libraries/jsonrpc4j/jsonrpc4j-sources.jar"/> <classpathentry kind="output" path="targets/classes"/> </classpath> diff --git a/datastore_server/resource/test-data/JsonDssServiceRpcGenericTest/.gitignore b/datastore_server/resource/test-data/JsonDssServiceRpcGenericTest/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/CrossOriginFilter.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/CrossOriginFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..58ec677827a8b5715967da03e5712b5cbbf1caad --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/CrossOriginFilter.java @@ -0,0 +1,72 @@ +/* + * Copyright 2011 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.generic.server; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider; + +/** + * Implements CORS (Cross Origin Resource Sharing) to allow a web page served from the openBIS AS to + * access resources on the DSS. + * <p> + * For more details on CORS see + * http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/. + * + * @author Kaloyan Enimanev + */ +public class CrossOriginFilter implements Filter +{ + private static final String ORIGIN_HEADER = "Origin"; + + private static final String ACCESS_CONTROL_ALLOW_ORIGIN_HEADER = "Access-Control-Allow-Origin"; + + private String openBisServerUrl; + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain filterChain) + throws IOException, ServletException + { + String originHeader = ((HttpServletRequest) request).getHeader(ORIGIN_HEADER); + if (originHeader != null && originHeader.startsWith(openBisServerUrl)) + { + ((HttpServletResponse) response).setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, + originHeader); + } + filterChain.doFilter(request, response); + } + + public void destroy() + { + } + + + public void init(FilterConfig arg0) throws ServletException + { + openBisServerUrl = ServiceProvider.getConfigProvider().getOpenBisServerUrl(); + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java index b0de8bf66039cacf93086d3c541ff3fd5401aab7..8b477701b0e93cffee605d260f9193614069d3e3 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreServer.java @@ -41,6 +41,7 @@ import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.server.ssl.SslConnector; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSocketConnector; +import org.eclipse.jetty.servlet.FilterMapping; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.springframework.context.i18n.LocaleContextHolder; @@ -50,6 +51,7 @@ import org.springframework.web.HttpRequestHandler; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.context.WebApplicationContext; +import com.googlecode.jsonrpc4j.spring.JsonServiceExporter; import com.marathon.util.spring.StreamSupportingHttpInvokerServiceExporter; import ch.systemsx.cisd.common.api.IRpcServiceNameServer; @@ -68,6 +70,7 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.DssSessionAuthorizationHolder; import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.IDssServiceRpcGenericInternal; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.DataStoreApiUrlUtilities; +import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.IDssServiceRpcGeneric; import ch.systemsx.cisd.openbis.dss.generic.shared.dto.PluginServletConfig; import ch.systemsx.cisd.openbis.dss.generic.shared.utils.DssPropertyParametersUtil; import ch.systemsx.cisd.openbis.generic.shared.IServer; @@ -226,7 +229,7 @@ public class DataStoreServer DatasetDownloadServlet.setDownloadUrl(configParams.getDownloadURL()); servletContextHandler.addServlet(DatasetDownloadServlet.class, applicationName + "/*"); - initializeRpcServices(servletContextHandler, applicationContext); + initializeRpcServices(servletContextHandler, applicationContext, configParams); registerPluginServlets(servletContextHandler, configParams.getPluginServlets()); registerImageOverviewServlet(servletContextHandler, configParams); } @@ -238,7 +241,7 @@ public class DataStoreServer // Perhaps by using Spring and the dssApplicationContext.xml more effectively, or perhaps by // using annotations and reflection. private static void initializeRpcServices(final ServletContextHandler context, - final ApplicationContext applicationContext) + final ApplicationContext applicationContext, ConfigParameters configParams) { // Get the spring bean and do some additional configuration StreamSupportingHttpInvokerServiceExporter v1ServiceExporter = @@ -255,6 +258,30 @@ public class DataStoreServer context.addServlet(new ServletHolder(new HttpInvokerServlet(v1ServiceExporter, rpcV1Path)), rpcV1Path); + // + // export the API via JSON + // + String jsonRpcV1Suffix = rpcV1Suffix + ".json"; + String jsonRpcV1Path = DataStoreApiUrlUtilities.getUrlForRpcService(jsonRpcV1Suffix); + JsonServiceExporter jsonV1ServiceExporter = new JsonServiceExporter(); + jsonV1ServiceExporter.setService(service); + jsonV1ServiceExporter.setServiceInterface(IDssServiceRpcGeneric.class); + jsonV1ServiceExporter + .setApplicationContext((org.springframework.context.ApplicationContext) ServiceProvider + .getApplicationContext()); + try + { + jsonV1ServiceExporter.afterPropertiesSet(); + } catch (Exception ex) + { + throw new RuntimeException("Cannot initialize json-rpc service exporter:" + + ex.getMessage(), ex); + } + + context.addServlet(new ServletHolder(new HttpInvokerServlet(jsonV1ServiceExporter, + jsonRpcV1Path)), jsonRpcV1Path); + context.addFilter(CrossOriginFilter.class, "/*", FilterMapping.ALL); + HttpInvokerServiceExporter nameServiceExporter = ServiceProvider.getRpcNameServiceExporter(); String nameServerPath = @@ -263,20 +290,26 @@ public class DataStoreServer context.addServlet(new ServletHolder(new HttpInvokerServlet(nameServiceExporter, nameServerPath)), nameServerPath); - RpcServiceInterfaceVersionDTO nameServerVersion = - new RpcServiceInterfaceVersionDTO(IRpcServiceNameServer.PREFFERED_SERVICE_NAME, - IRpcServiceNameServer.PREFFERED_URL_SUFFIX, 1, 0); // Inform the name server about the services I export // N.b. In the future, this could be done using spring instead of programmatically RpcServiceNameServer rpcNameServer = (RpcServiceNameServer) nameServiceExporter.getService(); + RpcServiceInterfaceVersionDTO nameServerVersion = + new RpcServiceInterfaceVersionDTO(IRpcServiceNameServer.PREFFERED_SERVICE_NAME, + IRpcServiceNameServer.PREFFERED_URL_SUFFIX, + rpcNameServer.getMajorVersion(), rpcNameServer.getMinorVersion()); RpcServiceInterfaceVersionDTO v1Interface = new RpcServiceInterfaceVersionDTO(DssServiceRpcGeneric.DSS_SERVICE_NAME, - rpcV1Suffix, 1, 0); - rpcNameServer.addSupportedInterfaceVersion(v1Interface); + rpcV1Suffix, service.getMajorVersion(), service.getMinorVersion()); + RpcServiceInterfaceVersionDTO jsonV1Interface = + new RpcServiceInterfaceVersionDTO(DssServiceRpcGeneric.DSS_SERVICE_NAME, + jsonRpcV1Suffix, service.getMajorVersion(), service.getMinorVersion()); + rpcNameServer.addSupportedInterfaceVersion(nameServerVersion); + rpcNameServer.addSupportedInterfaceVersion(v1Interface); + rpcNameServer.addSupportedInterfaceVersion(jsonV1Interface); } @SuppressWarnings("unchecked") diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/api/v1/FileInfoDssDTO.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/api/v1/FileInfoDssDTO.java index 6ce42b8da17bc52ab5af179c6b578b0c523f3c40..b5d5d5f14398f510a81b2ac004e1b876412508ed 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/api/v1/FileInfoDssDTO.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/api/v1/FileInfoDssDTO.java @@ -20,23 +20,25 @@ import java.io.Serializable; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; +import org.codehaus.jackson.annotate.JsonProperty; /** * Represents information about a file stored in DSS. * * @author Chandrasekhar Ramakrishnan */ +@SuppressWarnings("unused") public class FileInfoDssDTO implements Serializable { private static final long serialVersionUID = 1L; - private final String pathInDataSet; + private String pathInDataSet; - private final String pathInListing; + private String pathInListing; - private final boolean isDirectory; + private boolean isDirectory; - private final long fileSize; + private long fileSize; public FileInfoDssDTO(String pathInDataSet, String pathInListing, boolean isDirectory, long fileSize) @@ -66,6 +68,7 @@ public class FileInfoDssDTO implements Serializable /** * Return true if this FileInfo represents a folder. */ + @JsonProperty(value = "isDirectory") public boolean isDirectory() { return isDirectory; @@ -89,4 +92,32 @@ public class FileInfoDssDTO implements Serializable sb.append(getFileSize()); return sb.toString(); } + + // + // JSON-RPC + // + private FileInfoDssDTO() + { + + } + + private void setPathInDataSet(String pathInDataSet) + { + this.pathInDataSet = pathInDataSet; + } + + private void setPathInListing(String pathInListing) + { + this.pathInListing = pathInListing; + } + + private void setIsDirectory(boolean isDirectory) + { + this.isDirectory = isDirectory; + } + + private void setFileSize(long fileSize) + { + this.fileSize = fileSize; + } } diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/JsonDssServiceRpcGenericTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/JsonDssServiceRpcGenericTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e22a31ca2dea66e812d547c488e927e07854902b --- /dev/null +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/JsonDssServiceRpcGenericTest.java @@ -0,0 +1,121 @@ +/* + * Copyright 2011 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.datastoreserver.systemtests; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.commons.io.FileUtils; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.googlecode.jsonrpc4j.JsonRpcHttpClient; +import com.googlecode.jsonrpc4j.ProxyUtil; + +import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO; +import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.IDssServiceRpcGeneric; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService; + +/** + * Verifies that the functionality of {@link IDssServiceRpcGeneric} is accessible over JSON-RPC. + * + * @author Kaloyan Enimanev + */ +@Test(groups = + { "slow" }) +public class JsonDssServiceRpcGenericTest extends SystemTestCase +{ + private static final String OPENBIS_URL = "http://localhost:8888" + + IGeneralInformationService.JSON_SERVICE_URL; + + // TODO KE: put the suffix in a constant + private static final String DSS_URL = "http://localhost:8889" + + "/datastore_server/rmi-dss-api-v1.json"; + + private IGeneralInformationService openbisService; + + private IDssServiceRpcGeneric dssRpcService; + + private String sessionToken; + + @BeforeClass + public void beforeClass() throws IOException + { + openbisService = createOpenbisService(); + dssRpcService = createDssRpcService(); + + sessionToken = openbisService.tryToAuthenticateForAllServices("test", "1"); + + File resourceDir = + new File("../datastore_server/resource/test-data/" + getClass().getSimpleName()); + FileUtils.copyDirectory(resourceDir, rootDir); + } + + @AfterClass + public void afterClass() + { + openbisService.logout(sessionToken); + } + + @Test + public void testListDataSetContents() + { + + String validationScript = dssRpcService.getValidationScript(sessionToken, "HCS_IMAGE"); + System.out.println(validationScript); + + FileInfoDssDTO[] result = + dssRpcService.listFilesForDataSet(sessionToken, "20081105092159111-1", "", true); + + for (FileInfoDssDTO fileInfo : result) + { + System.out.println(fileInfo); + } + + } + + private IGeneralInformationService createOpenbisService() + { + try + { + JsonRpcHttpClient client = new JsonRpcHttpClient(new URL(OPENBIS_URL)); + return ProxyUtil.createProxy(getClass().getClassLoader(), + IGeneralInformationService.class, client); + } catch (MalformedURLException ex) + { + throw new RuntimeException("Failed to initialize json-rpc client: " + ex.getMessage(), + ex); + } + } + + private IDssServiceRpcGeneric createDssRpcService() + { + try + { + JsonRpcHttpClient client = new JsonRpcHttpClient(new URL(DSS_URL)); + return ProxyUtil.createProxy(getClass().getClassLoader(), IDssServiceRpcGeneric.class, + client); + } catch (MalformedURLException ex) + { + throw new RuntimeException("Failed to initialize json-rpc client: " + ex.getMessage(), + ex); + } + } +}