package org.ow2.chameleon.fuchsia.exporter.jsonrpc; /* * #%L * OW2 Chameleon - Fuchsia Exporter JSON-RPC * %% * Copyright (C) 2009 - 2014 OW2 Chameleon * %% * 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. * #L% */ import com.googlecode.jsonrpc4j.JsonRpcServer; import org.apache.felix.ipojo.annotations.*; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.service.http.HttpService; import org.osgi.service.http.NamespaceException; import org.ow2.chameleon.fuchsia.core.FuchsiaUtils; import org.ow2.chameleon.fuchsia.core.component.AbstractExporterComponent; import org.ow2.chameleon.fuchsia.core.component.ExporterService; import org.ow2.chameleon.fuchsia.core.component.ImporterService; import org.ow2.chameleon.fuchsia.core.declaration.ExportDeclaration; import org.ow2.chameleon.fuchsia.core.declaration.ImportDeclaration; import org.ow2.chameleon.fuchsia.core.exceptions.BinderException; import org.ow2.chameleon.fuchsia.exporter.jsonrpc.model.JSONRPCExportDeclarationWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; import static org.apache.felix.ipojo.Factory.INSTANCE_NAME_PROPERTY; /** * Provides an {@link ImporterService} allowing to access a. * remote endpoint through jsonrpc thanks to the jsonrpc4j implementation. * <p/> * A valid {@link ImportDeclaration} for this ImporterService contains has metadata : * - ID : a unique String which is the id of the JSON-RPC service * - URL : a String containing the URL of the JSON-RPC service to import into OSGi. * - SERVICE_CLASS : a String containing the name of the class to use to build the proxy * <p/> * TODO : Improves the client management, only one client should be created for a given uri. */ @Component @Provides(specifications = {ExporterService.class}) public class JSONRPCExporter extends AbstractExporterComponent { private static final Logger LOG = LoggerFactory.getLogger(JSONRPCExporter.class); @ServiceProperty(name = INSTANCE_NAME_PROPERTY) private String name; @ServiceProperty(name = "target", value = "(&(fuchsia.export.jsonrpc.instance=*)(scope=generic))") private String filter; @Requires HttpService web; private Set<String> registeredServlets = new HashSet<String>(); private final BundleContext context; private ServiceReference serviceReference; public JSONRPCExporter(BundleContext pContext) { context = pContext; } @Override protected void useExportDeclaration(ExportDeclaration exportDeclaration) throws BinderException { Class<?> klass = null; JSONRPCExportDeclarationWrapper jp = JSONRPCExportDeclarationWrapper.create(exportDeclaration); try { klass = FuchsiaUtils.loadClass(context, jp.getInstanceClass()); } catch (ClassNotFoundException e) { LOG.warn("Failed to load from the own bundle, loading externally", e); } String osgiFilter = String.format("(instance.name=%s)", jp.getInstanceName()); List<ServiceReference> references = null; try { references = new ArrayList<ServiceReference>(context.getServiceReferences(klass, osgiFilter)); } catch (InvalidSyntaxException e) { LOG.error("LDAP filter specified on the linker is not valid, recheck your LDAP filters for the linker and exporter. ", e); return; } Object serviceToBePublished = context.getService(references.iterator().next()); final JsonRpcServer jsonRpcServer = new JsonRpcServer(serviceToBePublished, klass); final String endpointURL = String.format("%s/%s", jp.getUrlContext(), jp.getInstanceName()); Servlet gs = new RPCServlet(jsonRpcServer); try { web.registerServlet(endpointURL, gs, new Hashtable(), null); exportDeclaration.handle(serviceReference); registeredServlets.add(endpointURL); LOG.info("JSONRPC-exporter, publishing object exporter at: {}", endpointURL); } catch (NamespaceException e) { LOG.error("Namespace failure", e); } catch (ServletException e) { LOG.error("Failed registering the servlet to respond the RPC request.", e); } } @Override protected void denyExportDeclaration(ExportDeclaration exportDeclaration) throws BinderException { JSONRPCExportDeclarationWrapper jp = JSONRPCExportDeclarationWrapper.create(exportDeclaration); final String endpointURL = String.format("%s/%s", jp.getUrlContext(), jp.getInstanceName()); exportDeclaration.unhandle(serviceReference); registeredServlets.remove(endpointURL); web.unregister(endpointURL); } public String getName() { return name; } @PostRegistration public void registration(ServiceReference serviceReference) { this.serviceReference = serviceReference; } /* * (non-Javadoc) * @see org.ow2.chameleon.rose.AbstractImporterComponent#stop() */ @Override @Invalidate public void stop() { unregisterAllServlets(); } /** * Unregister all servlets registered by this exporter. */ private void unregisterAllServlets() { for (String endpoint : registeredServlets) { registeredServlets.remove(endpoint); web.unregister(endpoint); LOG.info("endpoint {} unregistered", endpoint); } } static class RPCServlet extends HttpServlet { private final JsonRpcServer jsonRpcServer; public RPCServlet(JsonRpcServer jsonRpcServer) { this.jsonRpcServer = jsonRpcServer; } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { jsonRpcServer.handle(req, resp); } } }