/**
* 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
* @author Julien Eberle
*/
package org.openiot.gsn.http.rest;
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.security.KeyStore;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.openiot.gsn.Main;
import org.apache.http.*;
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.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
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.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
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;
import com.thoughtworks.xstream.XStreamException;
public class PushRemoteWrapper extends AbstractWrapper {
private static final int KEEP_ALIVE_PERIOD = 5000;
private final transient Logger logger = Logger.getLogger(PushRemoteWrapper.class);
private final XStream XSTREAM = StreamElement4Rest.getXstream();
private double uid = -1; //only set for push based delivery(default)
private RemoteWrapperParamParser initParams;
private DefaultHttpClient httpclient = new DefaultHttpClient();
private long lastReceivedTimestamp;
protected DataField[] structure;
List<NameValuePair> postParameters;
public void dispose() {
NotificationRegistry.getInstance().removeNotification(uid);
}
public boolean initialize() {
try {
initParams = new RemoteWrapperParamParser(getActiveAddressBean(), true);
uid = Math.random();
postParameters = new ArrayList<NameValuePair>();
postParameters.add(new BasicNameValuePair(PushDelivery.NOTIFICATION_ID_KEY, Double.toString(uid)));
postParameters.add(new BasicNameValuePair(PushDelivery.LOCAL_CONTACT_POINT, initParams.getLocalContactPoint()));
// 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 = registerAndGetStructure();
} catch (Exception e) {
logger.error(e.getMessage(), e);
NotificationRegistry.getInstance().removeNotification(uid);
return false;
}
return true;
}
public DataField[] getOutputFormat() {
return structure;
}
public String getWrapperName() {
return "Push-Remote Wrapper";
}
public DataField[] registerAndGetStructure() throws IOException, ClassNotFoundException {
// Create the POST request
HttpPost httpPost = new HttpPost(initParams.getRemoteContactPointEncoded(lastReceivedTimestamp));
// Add the POST parameters
httpPost.setEntity(new UrlEncodedFormEntity(postParameters, HTTP.UTF_8));
//
httpPost.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, Boolean.FALSE);
// Create local execution context
HttpContext localContext = new BasicHttpContext();
//
NotificationRegistry.getInstance().addNotification(uid, this);
int tries = 0;
AuthState authState = null;
//
while (tries < 2) {
tries++;
HttpResponse response = null;
try {
// Execute the POST request
response = httpclient.execute(httpPost, 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()));
structure = (DataField[]) XSTREAM.fromXML(response.getEntity().getContent());
logger.debug("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 POST 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 POST request.");
httpPost.abort();
throw ex;
}
finally {
if (response != null && response.getEntity() != null) {
response.getEntity().consumeContent();
}
}
}
if (structure == null)
throw new RuntimeException("Cannot connect to the remote host.");
return structure;
}
public boolean manualDataInsertion(String Xstream4Rest) {
logger.debug(new StringBuilder().append("Received Stream Element at the push wrapper."));
try {
StreamElement4Rest se = (StreamElement4Rest) XSTREAM.fromXML(Xstream4Rest);
StreamElement streamElement = se.toStreamElement();
// 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(streamElement)) {
lastReceivedTimestamp = streamElement.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(streamElement);
if (status)
lastReceivedTimestamp = streamElement.getTimeStamp();
return status;
}
catch (SQLException e) {
logger.warn(e.getMessage(), e);
return false;
}
catch (XStreamException e){
logger.warn(e.getMessage(), e);
return false;
}
}
public void run() {
HttpPost httpPost = new HttpPost(initParams.getRemoteContactPointEncoded(lastReceivedTimestamp));
//
httpPost.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, Boolean.FALSE);
//
HttpResponse response = null; //This is acting as keep alive.
//
while (isActive()) {
try {
Thread.sleep(KEEP_ALIVE_PERIOD);
httpPost.setEntity(new UrlEncodedFormEntity(postParameters, HTTP.UTF_8));
response = null;
response = httpclient.execute(httpPost);
int status = response.getStatusLine().getStatusCode();
if (status != RestStreamHanlder.SUCCESS_200) {
logger.error("Cant register to the remote client, retrying in:" + (KEEP_ALIVE_PERIOD / 1000) + " seconds.");
structure = registerAndGetStructure();
}
} catch (Exception e) {
logger.warn(e.getMessage(), e);
} finally {
if (response != null) {
try {
response.getEntity().getContent().close();
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
}
}
}
}
}