/** * Copyright (c) 2011-2014, OpenIoT * * This file is part of OpenIoT. * * OpenIoT is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * OpenIoT 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 OpenIoT. If not, see <http://www.gnu.org/licenses/>. * * Contact: OpenIoT mailto: info@openiot.eu * @author Ali Salehi * @author Mehdi Riahi * @author Timotee Maret */ package org.openiot.gsn.http.rest; import org.openiot.gsn.Main; import org.openiot.gsn.beans.ContainerConfig; import org.openiot.gsn.beans.DataField; import org.openiot.gsn.beans.StreamElement; import org.openiot.gsn.wrappers.AbstractWrapper; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.security.KeyStore; import java.sql.SQLException; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthState; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.ClientContext; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.apache.log4j.Logger; import com.thoughtworks.xstream.XStream; public class RestRemoteWrapper extends AbstractWrapper { private final XStream XSTREAM = StreamElement4Rest.getXstream(); private final transient Logger logger = Logger.getLogger(RestRemoteWrapper.class); private DataField[] structure = null; private DefaultHttpClient httpclient; private long lastReceivedTimestamp = -1; private ObjectInputStream inputStream; private HttpResponse response; private HttpParams getHttpClientParams(int timeout) { HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET); HttpProtocolParams.setUseExpectContinue(params, true); HttpConnectionParams.setTcpNoDelay(params, false); HttpConnectionParams.setSocketBufferSize(params, 8192); HttpConnectionParams.setStaleCheckingEnabled(params, true); HttpConnectionParams.setConnectionTimeout(params, 30 * 1000); // Set the connection time to 30s HttpConnectionParams.setSoTimeout(params, timeout); HttpProtocolParams.setUserAgent(params, "GSN-HTTP-CLIENT"); return params; } public DataField[] getOutputFormat() { return structure; } private RemoteWrapperParamParser initParams; public String getWrapperName() { return "Rest Remote Wrapper"; } public boolean initialize() { try { initParams = new RemoteWrapperParamParser(getActiveAddressBean(), false); httpclient = new DefaultHttpClient(getHttpClientParams(initParams.getTimeout())); // Init the http client if (initParams.isSSLRequired()) { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(new FileInputStream(new File("conf/servertestkeystore")), Main.getContainerConfig().getSSLKeyStorePassword().toCharArray()); SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore); socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); int sslPort = Main.getContainerConfig().getSSLPort() > 0 ? Main.getContainerConfig().getSSLPort() : ContainerConfig.DEFAULT_SSL_PORT; Scheme sch = new Scheme("https", socketFactory, sslPort); httpclient.getConnectionManager().getSchemeRegistry().register(sch); } Scheme plainsch = new Scheme("http", PlainSocketFactory.getSocketFactory(), Main.getContainerConfig().getContainerPort()); httpclient.getConnectionManager().getSchemeRegistry().register(plainsch); // lastReceivedTimestamp = initParams.getStartTime(); structure = connectToRemote(); } catch (Exception e) { logger.error(e.getMessage(), e); return false; } return true; } public DataField[] connectToRemote() throws IOException, ClassNotFoundException { // Create the GET request HttpGet httpget = new HttpGet(initParams.getRemoteContactPointEncoded(lastReceivedTimestamp)); // Create local execution context HttpContext localContext = new BasicHttpContext(); // structure = null; int tries = 0; AuthState authState = null; // if (inputStream != null) { try { if(response != null && response.getEntity() != null) { response.getEntity().consumeContent(); } inputStream.close(); inputStream = null; } catch (Exception e) { logger.debug(e.getMessage(), e); } } // while (tries < 2) { tries++; try { // Execute the GET request response = httpclient.execute(httpget, localContext); // int sc = response.getStatusLine().getStatusCode(); // if (sc == HttpStatus.SC_OK) { logger.debug(new StringBuilder().append("Wants to consume the structure packet from ").append(initParams.getRemoteContactPoint())); inputStream = XSTREAM.createObjectInputStream(response.getEntity().getContent()); structure = (DataField[]) inputStream.readObject(); logger.warn("Connection established for: " + initParams.getRemoteContactPoint()); break; } else { if (sc == HttpStatus.SC_UNAUTHORIZED) authState = (AuthState) localContext.getAttribute(ClientContext.TARGET_AUTH_STATE); // Target host authentication required else if (sc == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) authState = (AuthState) localContext.getAttribute(ClientContext.PROXY_AUTH_STATE); // Proxy authentication required else { logger.error(new StringBuilder() .append("Unexpected GET status code returned: ") .append(sc) .append("\nreason: ") .append(response.getStatusLine().getReasonPhrase())); } if (authState != null) { if (initParams.getUsername() == null || (tries > 1 && initParams.getUsername() != null)) { logger.error("A valid username/password required to connect to the remote host: " + initParams.getRemoteContactPoint()); } else { AuthScope authScope = authState.getAuthScope(); logger.warn(new StringBuilder().append("Setting Credentials for host: ").append(authScope.getHost()).append(":").append(authScope.getPort())); Credentials creds = new UsernamePasswordCredentials(initParams.getUsername(), initParams.getPassword()); httpclient.getCredentialsProvider().setCredentials(authScope, creds); } } } } catch (RuntimeException ex) { // In case of an unexpected exception you may want to abort // the HTTP request in order to shut down the underlying // connection and release it back to the connection manager. logger.warn("Aborting the HTTP GET request."); httpget.abort(); throw ex; } finally { if (structure == null) { if(response != null && response.getEntity() != null) { response.getEntity().consumeContent(); } } } } if (structure == null) throw new RuntimeException("Cannot connect to the remote host: " + initParams.getRemoteContactPoint()); return structure; } public void dispose() { try { httpclient.getConnectionManager().shutdown(); //This closes the connection already in use by the response } catch (Exception e) { logger.debug(e.getMessage(), e); } } public void run() { StreamElement4Rest se = null; while (isActive()) { try { while (isActive() && (se = (StreamElement4Rest) inputStream.readObject()) != null) { StreamElement streamElement = se.toStreamElement(); if ( ! (streamElement.getFieldNames().length == 1 && streamElement.getFieldNames()[0].equals("keepalive"))) { boolean status = manualDataInsertion(streamElement); if (!status && inputStream != null) { response.getEntity().consumeContent(); inputStream.close(); inputStream = null; } } else logger.debug("Received a keep alive message."); } } catch (Exception e) { logger.warn("Connection to the remote host: " + initParams.getRemoteContactPoint() + " is lost, trying to reconnect in 3 seconds..."); try { if (isActive()) { Thread.sleep(3000); connectToRemote(); } } catch (Exception err) { logger.debug(err.getMessage(), err); } } } } public boolean manualDataInsertion(StreamElement se) { try { // If the stream element is out of order, we accept the stream element and wait for the next (update the last received time and return true) if (isOutOfOrder(se)) { lastReceivedTimestamp = se.getTimeStamp(); return true; } // Otherwise, we first try to insert the stream element. // If the stream element was inserted succesfully, we wait for the next, // otherwise, we return false. boolean status = postStreamElement(se); if (status) lastReceivedTimestamp = se.getTimeStamp(); return status; } catch (SQLException e) { logger.warn(e.getMessage(), e); return false; } } }