at.spardat.xma.boot.transport.HTTPTransport Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* s IT Solutions AT Spardat GmbH - initial API and implementation
*******************************************************************************/
/*
* Created on 16.05.2003
*/
package at.spardat.xma.boot.transport;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.SimpleTimeZone;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.cookie.CookieSpec;
import org.apache.commons.httpclient.cookie.CookieSpecBase;
import org.apache.commons.httpclient.cookie.MalformedCookieException;
import at.spardat.xma.boot.BootRuntime;
import at.spardat.xma.boot.Statics;
import at.spardat.xma.boot.comp.CCLoader;
import at.spardat.xma.boot.component.IRtXMASessionClient;
import at.spardat.xma.boot.logger.LogLevel;
import at.spardat.xma.boot.logger.Logger;
/**
* This class implements low level transport on http transport using suns UrlConnection implementation.
*
* @author s2877, s3595
* @version $Id: HTTPTransport.java 7128 2011-01-26 07:41:09Z hoenninger $
*
*/
public class HTTPTransport extends Transport {
/** used to format if-modified-since correctly */
private static DateFormat httpdate_;
private static Logger log_;
private static HostnameVerifier hostnameVerifier;
private static CookieSpec cookieSpec;
private static HttpState httpState = new HttpState();
{
httpdate_ = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US ); //$NON-NLS-1$
httpdate_.setTimeZone(new SimpleTimeZone(0, "GMT")); //$NON-NLS-1$
}
/**
* Initializes the underlaying http-protocol-provider from the given Propterties.
* This sets the tcp-timeouts, the proxy-settings and ssl-settings.
* @param prop containing properties for http and https protocol
*/
public static void init( Properties prop ){
log_ = Logger.getLogger( "boot.transport.http" ); //$NON-NLS-1$
// proxy properties
String strProxyEnable = prop.getProperty( Statics.CFG_PROP_PROXYENABLE );
if( strProxyEnable != null && Boolean.valueOf(strProxyEnable).booleanValue()) {
String strProxyServer = prop.getProperty( Statics.CFG_PROP_PROXYSERVER );
String strProxyPort = prop.getProperty( Statics.CFG_PROP_PROXYPORT );
if( strProxyServer!=null && strProxyPort!=null ) {
System.setProperty( "proxySet", "true" );
System.setProperty( "http.proxyHost", strProxyServer );
System.setProperty( "http.proxyPort", strProxyPort );
log_.log(LogLevel.FINE, "transport proxy is: {0}:{1}", new Object[] { strProxyServer, strProxyPort } );
}
String strSecureProxyServer = prop.getProperty( Statics.CFG_PROP_SECUREPROXYSERVER );
String strSecureProxyPort = prop.getProperty( Statics.CFG_PROP_SECUREPROXYPORT );
if(strSecureProxyPort!=null&&strSecureProxyServer!=null) {
System.setProperty("https.proxyHost",strSecureProxyServer);
System.setProperty("https.proxyPort",strSecureProxyPort);
log_.log(LogLevel.FINE, "secure transport proxy is: {0}:{1}", new Object[] { strSecureProxyServer, strSecureProxyPort } );
}
String strProxyOverride = prop.getProperty( Statics.CFG_PROP_PROXYOVERRIDE );
if(strProxyOverride!=null) {
strProxyOverride = strProxyOverride.replace(';','|'); // documented delimiter for IE
strProxyOverride = strProxyOverride.replace(',','|'); // IE supports ',' as delimiter, too
strProxyOverride = strProxyOverride.replace(' ','|'); // IE supports blank as delimiter, too
strProxyOverride = strProxyOverride.replace('\t','|'); // IE supports tab as delimiter, too
strProxyOverride = strProxyOverride.replace('\r','|'); // IE supports carriage return as delimiter, too
strProxyOverride = strProxyOverride.replace('\n','|'); // IE supports newline as delimiter, too
strProxyOverride = strProxyOverride.replaceAll("","localhost|127.0.0.1");
System.setProperty("http.nonProxyHosts",strProxyOverride);
log_.log(LogLevel.FINE, "proxy not used for: {0}", strProxyOverride );
}
} else {
log_.log(LogLevel.FINE, "no transport proxy is used" );
}
// timeout properties
String strConnectTimeout = prop.getProperty(Statics.CFG_PROP_CONNECTTIMEOUT);
if(strConnectTimeout!=null) {
System.setProperty("sun.net.client.defaultConnectTimeout",strConnectTimeout);
log_.log(LogLevel.FINE, "http connect timeout: "+strConnectTimeout+" milliseconds");
}
String strReadTimeout = prop.getProperty(Statics.CFG_PROP_READTIMEOUT);
if(strReadTimeout!=null) {
System.setProperty("sun.net.client.defaultReadTimeout",strReadTimeout);
log_.log(LogLevel.FINE, "http read timeout: "+strReadTimeout+" milliseconds");
}
// ssl properties
String strTrustStore = prop.getProperty(Statics.CFG_PROP_SECURECERTS);
if(strTrustStore!=null) {
log_.log(LogLevel.FINE,"using trusted certificates file "+strTrustStore);
File trustFile = new File(strTrustStore);
if(!trustFile.exists()) {
log_.log(LogLevel.SEVERE,"trusted certificates file '"+trustFile.getAbsolutePath()+"' not found");
}
System.setProperty("javax.net.ssl.trustStore",strTrustStore);
}
hostnameVerifier = new HostnameVerifierImpl(prop.getProperty(Statics.CFG_PROP_HOSTNAMEVERIFYIGNORE));
// cookie handling policy
Class cookiePolicyClass = null;
String strCookiePolicy = prop.getProperty(Statics.CFG_PROP_COOKIEPOLICY);
if(strCookiePolicy!=null) {
try {
cookiePolicyClass = Class.forName(strCookiePolicy);
} catch (ClassNotFoundException e) {
log_.log(LogLevel.WARNING,"configured cookiePolicy '"+strCookiePolicy+"' not found, using default");
}
}
if(cookiePolicyClass==null) {
cookiePolicyClass = CookieSpecBase.class;
}
log_.log(LogLevel.FINE,"using cookiePolicy "+cookiePolicyClass.getName());
CookiePolicy.registerCookieSpec(CookiePolicy.DEFAULT, cookiePolicyClass);
cookieSpec=CookiePolicy.getDefaultSpec();
}
/**
* logs the proxy settings at the given level.
*/
public void logProxyInfo(LogLevel level) {
StringBuffer result = new StringBuffer();
String proxyHost = System.getProperty("http.proxyHost");
if(proxyHost!=null) {
result.append("http proxy is: "+proxyHost);
String proxyPort = System.getProperty("http.proxyPort");
if(proxyPort!=null) {
result.append(":"+proxyPort);
}
log_.log(level,result.toString());
} else {
log_.log(level,"no http proxy used");
}
result = new StringBuffer();
proxyHost = System.getProperty("https.proxyHost");
if(proxyHost!=null) {
result.append("https proxy is: "+proxyHost);
String proxyPort = System.getProperty("https.proxyPort");
if(proxyPort!=null) {
result.append(":"+proxyPort);
}
log_.log(level,result.toString());
} else {
log_.log(level,"no https proxy used");
}
String nonProxyHosts = System.getProperty("http.nonProxyHosts");
if(nonProxyHosts!=null) {
log_.log(level,"no proxy used for: "+nonProxyHosts);
}
}
/**
* Constructs a HTTPTransport
.
*
*/
public HTTPTransport() {
if( log_==null)
log_ = Logger.getLogger( "boot.httpTransport" ); //$NON-NLS-1$
}
/**
* Converts a java.util.Date
to a String using the encoding specified by
* the HTTP-Specification. We have to wrap this task, cause we may have to change it.
* see also SUN-Bug_ID: 4397096
*
* @param date the date to be formated
* @return String the formated representation
*/
public static String httpDate(Date date) {
return httpdate_.format(date);
}
/**
* Converts a long containing a date to a String using the encoding specified by
* the HTTP-Specification. We have to wrap this task, cause we may have to change it.
* see also SUN-Bug_ID: 4397096
*
* @param ldate the milliseconds since January 1, 1970, 00:00:00 GMT.
* @return String the formated representation
*/
public static String httpDate(long ldate ) {
return httpdate_.format( new Date(ldate));
}
/* (non-Javadoc)
* @see at.spardat.xma.transport.Transport#getResource(at.spardat.xma.transport.XMA_URI, long)
*/
public Result getResource(final IRtXMASessionClient session, final XMA_URI resource, final long modifiedSince, final String etag) throws CommunicationException {
CCLoader swtCLoader = BootRuntime.getInstance().getAppManager().getSWTClassLoader();
if(swtCLoader!=null) {
class Inner implements Runnable {
Result result;
CommunicationException exc;
public void run() {
try {
result = getResourceImpl(session,resource,modifiedSince,etag);
} catch(CommunicationException exc) {
this.exc=exc;
}
}
}
Inner inner = new Inner();
try { // call BusyIndicator.showWhile over reflection from the SWT-Classloader
Class busyIndicatorClass = swtCLoader.loadClass("org.eclipse.swt.custom.BusyIndicator");
Class displayClass = swtCLoader.loadClass("org.eclipse.swt.widgets.Display");
Method method = busyIndicatorClass.getMethod("showWhile",new Class[]{displayClass,Runnable.class});
method.invoke(null,new Object[]{null,inner});
} catch (Exception exc) {
throw new RuntimeException(exc);
}
if(inner.exc!=null) throw inner.exc;
return inner.result;
} else {
return getResourceImpl(session,resource,modifiedSince,etag);
}
}
private Result getResourceImpl(IRtXMASessionClient session, XMA_URI resource, long modifiedSince, String etag) throws CommunicationException {
/* locals ---------------------------------- */
Result result = new Result();
int code = 0;
URL url = resource.getHTTP_URI();
HttpURLConnection conn;
/* locals ---------------------------------- */
try {
conn = (HttpURLConnection) url.openConnection();
if(conn instanceof HttpsURLConnection) {
((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier);
}
sendCookies(session,url,conn);
if( etag != null ){
conn.setRequestProperty("If-None-Match", etag); //$NON-NLS-1$
}
String strUrl = url.toExternalForm();
if( url.getQuery()==null && ( strUrl.endsWith( ".jar") || strUrl.endsWith( ".xml")) ) {
conn.setRequestProperty(Statics.HTTP_CACHE_CONTROL, Statics.HTTP_MAX_AGE + "=0"); //$NON-NLS-1$
}
if(modifiedSince>0) {
// see sun bugid: 4397096
// if HTTP_Util library is used, the original method may also be used.
// conn.setIfModifiedSince(modifiedSince);
conn.setRequestProperty( Statics.strIfModifiedSince, HTTPTransport.httpDate(modifiedSince));
}
conn.setRequestProperty( Statics.HTTP_ACCEPT,"*/*"); //$NON-NLS-1$
conn.setRequestProperty( Statics.HTTP_USER_AGENT, Statics.HTTP_USER_AGENT_NAME);
} catch (IOException exc) {
log_.log(LogLevel.WARNING, "error loading '"+url.toString()+"' form server:", exc); //$NON-NLS-1$
throw new ConnectException("error loading '"+url.toString()+"' form server:",exc);
}
try {
code = conn.getResponseCode();
if(code==HttpURLConnection.HTTP_NOT_MODIFIED) {
result.contentLength_ = 0;
result.lastModified_ = conn.getLastModified();
if(result.lastModified_<=0) {
result.lastModified_ = modifiedSince;
}
result.expirationDate_ = conn.getExpiration();
result.etag_ = conn.getHeaderField( Statics.strEtag );
if(result.etag_==null) {
result.etag_ = etag;
}
log_.log(LogLevel.FINE, "resource not modified: {0}", url.toExternalForm()); //$NON-NLS-1$
} else if(code==HttpURLConnection.HTTP_OK){
result.contentLength_ = conn.getContentLength();
result.lastModified_ = conn.getLastModified();
result.expirationDate_ = conn.getExpiration();
result.etag_ = conn.getHeaderField( Statics.strEtag );
result.transformations_= conn.getHeaderField(Statics.TRANSFORM_HEADER);
result.setBuffer( this.readOutput(conn) );
if(result.contentLength_<0) {
result.contentLength_=result.buffer_.length;
}
} else {
if(code<500) throw new ConnectException("error loading '"+url.toString()+"' from the server:",code);
else throw new ServerException("error loading '"+url.toString()+"' from the server:",code);
}
readCookies(session,url,conn);
} catch (CommunicationException ce) {
if(code!=0) log_.log(LogLevel.WARNING,"http returncode: {0}",Integer.toString(code)); //$NON-NLS-1$
log_.log(LogLevel.WARNING, "error loading '"+url.toString()+"' from the server:", ce); //$NON-NLS-1$
throw ce;
} catch (Exception ex) {
if(code!=0) log_.log(LogLevel.WARNING,"http returncode: {0}",Integer.toString(code)); //$NON-NLS-1$
log_.log(LogLevel.WARNING,"error loading '"+url.toString()+"' from the server:", ex); //$NON-NLS-1$
if(code<500) throw new ConnectException("error loading '"+url.toString()+"' from the server:",ex);
else throw new ServerException("error loading '"+url.toString()+"' from the server:",ex);
}
return result;
}
/* (non-Javadoc)
* @see at.spardat.xma.transport.Transport#callServerEvent(at.spardat.xma.session.XMASessionClient, at.spardat.xma.transport.XMA_URI, java.io.InputStream)
*/
public byte[] callServerEvent(final IRtXMASessionClient session,final XMA_URI eventHandler,final byte[] input) throws CommunicationException {
CCLoader swtCLoader = BootRuntime.getInstance().getAppManager().getSWTClassLoader();
if(swtCLoader!=null) {
class Inner implements Runnable {
byte[] result;
CommunicationException exc;
public void run() {
try {
result = callServerEventImpl(session,eventHandler,input);
} catch(CommunicationException exc) {
this.exc=exc;
}
}
}
Inner inner = new Inner();
try { // call BusyIndicator.showWhile over reflection from the SWT-Classloader
Class busyIndicatorClass = swtCLoader.loadClass("org.eclipse.swt.custom.BusyIndicator");
Class displayClass = swtCLoader.loadClass("org.eclipse.swt.widgets.Display");
Method method = busyIndicatorClass.getMethod("showWhile",new Class[]{displayClass,Runnable.class});
method.invoke(null,new Object[]{null,inner});
} catch (Exception exc) {
throw new RuntimeException(exc);
}
if(inner.exc!=null) throw inner.exc;
return inner.result;
} else {
return callServerEventImpl(session,eventHandler,input);
}
}
private byte[] callServerEventImpl(IRtXMASessionClient session, XMA_URI eventHandler, byte[] input) throws CommunicationException {
OutputStream serverIn;
int code = 0;
URL url = eventHandler.getHTTP_URI();
HttpURLConnection conn;
byte[] buffer = null;
try {
conn = (HttpURLConnection) url.openConnection();
if(conn instanceof HttpsURLConnection) {
((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier);
}
conn.setDoOutput(true);
conn.setRequestMethod("POST"); //$NON-NLS-1$
sendCookies(session,url,conn);
conn.setRequestProperty( Statics.HTTP_CONTENT_TYPE,"application/octet-stream"); //$NON-NLS-1$
conn.setRequestProperty( Statics.HTTP_ACCEPT,"application/octet-stream"); //$NON-NLS-1$
conn.setRequestProperty( Statics.HTTP_USER_AGENT , Statics.HTTP_USER_AGENT_NAME );
serverIn = conn.getOutputStream();
} catch (IOException exc) {
log_.log(LogLevel.WARNING,"error calling '"+url.toString()+"' at the server:", exc); //$NON-NLS-1$
throw new ConnectException("error calling '"+url.toString()+"' at the server:",exc);
}
try {
serverIn.write(input);
serverIn.close();
code = conn.getResponseCode();
buffer = this.readOutput(conn);
readCookies(session,url,conn);
return buffer;
} catch (CommunicationException ce) {
if(code!=0) log_.log(LogLevel.WARNING,"http returncode: {0}",Integer.toString(code)); //$NON-NLS-1$
log_.log(LogLevel.WARNING, "error calling '"+url.toString()+"' at the server:", ce); //$NON-NLS-1$
throw ce;
} catch (Exception ex) {
if(code!=0) log_.log(LogLevel.WARNING,"http returncode: {0}",Integer.toString(code)); //$NON-NLS-1$
log_.log(LogLevel.WARNING,"error calling '"+url.toString()+"' at the server:", ex); //$NON-NLS-1$
if(code<500) throw new ConnectException("error calling '"+url.toString()+"' at the server:",ex);
else throw new ServerException("error calling '"+url.toString()+"' at the server:",ex);
}
}
/**
* get server output into a buffer
*
* @param conn connection to read from
* @return byte[] server output
* @throws ServerException content lenght errro
* @throws IOException for read erros
*/
private byte[] readOutput( HttpURLConnection conn ) throws IOException {
InputStream serverOut = null;
byte[] buffer = null;
try{
serverOut = conn.getInputStream();
int len = conn.getContentLength();
int read = 0;
int all = 0;
if(len>-1) {
buffer = new byte[len];
while(read>-1&&all-1) all+=read;
}
if(read<0) {
throw new ServerException("Server reported contentLength "+len+" but send only "+all+" bytes of data");
}
return buffer;
} else {
// if no content length was send, use default size and grow dynamically
List bufferList = null;
final int defLen = 1024*8;
int lastLength = 0;
bufferList = new ArrayList();
for(;read>-1;all+=lastLength) {
buffer = new byte[defLen];
for(lastLength=0;read>-1&&lastLength-1) lastLength+=read;
}
bufferList.add(buffer);
}
byte [] result = new byte[all];
for(int i=0;i0) {
cookies = cookieSpec.match(url.getHost(),getPort(url),url.getPath(),"https".equals(url.getProtocol()),cookies);
}
return cookies;
}
/**
* Takes the cookies from the httpState which match the given url and creates the
* corresponsing cookie-header on the given connection.
*/
private void sendCookies(IRtXMASessionClient session, URL url,HttpURLConnection conn) {
Cookie[] cookies = getCookies(url,session);
if(cookies!=null&&cookies.length>0) {
String cookieHeader = cookieSpec.formatCookies(cookies);
conn.setRequestProperty(Statics.HTTP_COOKIE,cookieHeader);
if(session!=null && session.getId()==null) { // server side http session was established before client side session was created
for(int j=0;j