Java tutorial
/** * Copyright 2014 isandlaTech * * 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 org.cohorte.remote.dispatcher.servlet; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.util.Collection; import java.util.LinkedList; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.apache.felix.ipojo.annotations.Bind; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Instantiate; import org.apache.felix.ipojo.annotations.Invalidate; import org.apache.felix.ipojo.annotations.Property; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Requires; import org.apache.felix.ipojo.annotations.ServiceController; import org.apache.felix.ipojo.annotations.Validate; import org.cohorte.remote.ExportEndpoint; import org.cohorte.remote.IDispatcherServlet; import org.cohorte.remote.IExportsDispatcher; import org.cohorte.remote.IImportsRegistry; import org.cohorte.remote.ImportEndpoint; import org.cohorte.remote.dispatcher.beans.PelixEndpointDescription; import org.cohorte.remote.utilities.RSUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.osgi.service.http.HttpService; import org.osgi.service.log.LogService; /** * Registers the servlet service to the first HTTP service seen * * @author Thomas Calmant */ @Component(name = "cohorte-remote-dispatcher-servlet-factory") @Provides(specifications = IDispatcherServlet.class) @Instantiate(name = "cohorte-remote-dispatcher-servlet") public class ServletWrapper implements IDispatcherServlet { /** HTTP service port property */ private static final String HTTP_SERVICE_PORT = "org.osgi.service.http.port"; /** HTTPService dependency ID */ private static final String IPOJO_ID_HTTP = "http.service"; /** Exported endpoints dispatcher */ @Requires private IExportsDispatcher pDispatcher; /** The HTTP server port */ private int pHttpPort; /** The HTTP service */ @Requires(id = IPOJO_ID_HTTP, filter = "(" + HTTP_SERVICE_PORT + "=*)") private HttpService pHttpService; /** Log service */ @Requires private LogService pLogger; /** Imported services registry */ @Requires private IImportsRegistry pRegistry; /** The service controller */ @ServiceController private boolean pServiceController; /** The servlet object */ private RegistryServlet pServlet; /** The servlet registration path */ @Property(name = "servlet.path", value = "/pelix-dispatcher") private String pServletPath; /** * HTTP service ready * * @param aHttpService * The bound service * @param aServiceProperties * The HTTP service properties */ @Bind(id = IPOJO_ID_HTTP) private void bindHttpService(final HttpService aHttpService, final Map<?, ?> aServiceProperties) { final Object rawPort = aServiceProperties.get(HTTP_SERVICE_PORT); if (rawPort instanceof Number) { // Get the integer pHttpPort = ((Number) rawPort).intValue(); } else if (rawPort instanceof CharSequence) { // Parse the string pHttpPort = Integer.parseInt(rawPort.toString()); } else { // Unknown port type pLogger.log(LogService.LOG_WARNING, "Couldn't read access port " + rawPort); pHttpPort = -1; } } /* * (non-Javadoc) * * @see org.cohorte.remote.pelix.IDispatcherServlet#getPath() */ @Override public String getPath() { return pServletPath; } /* * (non-Javadoc) * * @see org.cohorte.remote.pelix.IDispatcherServlet#getPort() */ @Override public int getPort() { return pHttpPort; } /** * Returns the response of a HTTP server, or throws an exception * * @param aAddress * Server address * @param aPort * Server port * @param aPath * Request URI * @return The raw response of the server */ private String grabData(final InetAddress aAddress, final int aPort, final String aPath) { // Forge the URL final URL url; try { url = new URL("http", aAddress.getHostAddress(), aPort, aPath); } catch (final MalformedURLException ex) { pLogger.log(LogService.LOG_ERROR, "Couldn't forge the URL to access: " + aAddress + " : " + aPort + " - " + aPath, ex); return null; } // Open the connection HttpURLConnection httpConnection = null; try { httpConnection = (HttpURLConnection) url.openConnection(); httpConnection.connect(); // Flush the request final int responseCode = httpConnection.getResponseCode(); if (responseCode != HttpServletResponse.SC_OK) { // Incorrect answer pLogger.log(LogService.LOG_WARNING, "Error: " + url + " responded with code " + responseCode); return null; } // Get the response content final byte[] rawResult = RSUtils.inputStreamToBytes(httpConnection.getInputStream()); // Construct corresponding string return new String(rawResult); } catch (final IOException ex) { // Connection error pLogger.log(LogService.LOG_ERROR, "Error requesting information from " + url.toString(), ex); } finally { if (httpConnection != null) { httpConnection.disconnect(); } } return null; } /* * (non-Javadoc) * * @see * org.cohorte.remote.pelix.IDispatcherServlet#grabEndpoint(java.net.InetAddress * , int, java.lang.String, java.lang.String) */ @Override public ImportEndpoint grabEndpoint(final InetAddress aAddress, final int aPort, final String aPath, final String aEndpointUID) { // Get the raw servlet result final String rawResponse = grabData(aAddress, aPort, aPath + "/endpoint/" + aEndpointUID); if (rawResponse == null || rawResponse.isEmpty()) { // No response pLogger.log(LogService.LOG_WARNING, "No response from the server " + aAddress + " for end point " + aEndpointUID); return null; } try { // Parse it final JSONObject rawEndpoint = new JSONObject(rawResponse); // Convert the result final PelixEndpointDescription endpoint = new PelixEndpointDescription(rawEndpoint); endpoint.setServerAddress(aAddress.getHostAddress()); return endpoint.toImportEndpoint(); } catch (final JSONException ex) { // Invalid response pLogger.log(LogService.LOG_WARNING, "Invalid response from the server " + aAddress + " for end point " + aEndpointUID + "\n" + rawResponse, ex); } return null; } /** * Component invalidated */ @Invalidate public void invalidate() { if (pServlet != null) { pHttpService.unregister(pServletPath); pServlet = null; } pLogger.log(LogService.LOG_INFO, "Dispatcher servlet gone"); } /* * (non-Javadoc) * * @see * org.cohorte.remote.pelix.IDispatcherServlet#sendDiscovered(java.lang. * String, int, java.lang.String) */ @Override public void sendDiscovered(final String aHost, final int aPort, final String aPath) { // Prepare our endpoints final Collection<Map<String, Object>> endpointsMaps = new LinkedList<Map<String, Object>>(); for (final ExportEndpoint endpoint : pDispatcher.getEndpoints()) { endpointsMaps.add(endpoint.toMap()); } // Convert the list to JSON final String data = new JSONArray(endpointsMaps).toString(); // Prepare the path to the servlet endpoints final StringBuilder servletPath = new StringBuilder(aPath); if (!aPath.endsWith("/")) { servletPath.append("/"); } servletPath.append("endpoints"); final URL url; try { url = new URL("http", aHost, aPort, servletPath.toString()); } catch (final MalformedURLException ex) { pLogger.log(LogService.LOG_ERROR, "Error forging URL to send a discovered packet: " + ex, ex); return; } // Send a POST request HttpURLConnection httpConnection = null; try { httpConnection = (HttpURLConnection) url.openConnection(); // POST message httpConnection.setRequestMethod("POST"); httpConnection.setUseCaches(false); httpConnection.setDoInput(true); httpConnection.setDoOutput(true); // Headers httpConnection.setRequestProperty("Content-Type", "application/json"); // After fields, before content httpConnection.connect(); // Write the event in the request body, if any final OutputStream outStream = httpConnection.getOutputStream(); try { outStream.write(data.getBytes()); outStream.flush(); } finally { // Always be nice... outStream.close(); } // Flush the request final int responseCode = httpConnection.getResponseCode(); final String responseData = new String(RSUtils.inputStreamToBytes(httpConnection.getInputStream())); if (responseCode != HttpURLConnection.HTTP_OK) { pLogger.log(LogService.LOG_WARNING, "Error sending a 'discovered' packet: " + responseCode + " - " + responseData); return; } } catch (final IOException ex) { pLogger.log(LogService.LOG_ERROR, "Error sending the 'discovered' packet: " + ex, ex); } finally { // Clean up if (httpConnection != null) { httpConnection.disconnect(); } } } /** * Component validated */ @Validate public void validate() { // Preparation: deactivate the service pServiceController = false; // Set up the servlet pServlet = new RegistryServlet(pRegistry, pDispatcher); try { pHttpService.registerServlet(pServletPath, pServlet, null, null); } catch (final Exception ex) { pLogger.log(LogService.LOG_ERROR, "Error registering the dispatcher servlet. Abandon.", ex); invalidate(); return; } pLogger.log(LogService.LOG_INFO, "Dispatcher servlet ready"); // No error: activate the service pServiceController = true; } }