//$HeadURL$
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2008 by:
Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library 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; either
version 2.1 of the License, or (at your option) any later version.
This library 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 this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.igeo.modules.remotecontrol;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.util.KVP2Map;
import org.deegree.igeo.ApplicationContainer;
import org.deegree.igeo.config.ModuleType;
import org.deegree.igeo.config._ComponentPositionType;
import org.deegree.igeo.i18n.Messages;
import org.deegree.igeo.modules.ActionDescription;
import org.deegree.igeo.modules.DefaultModule;
import org.deegree.igeo.modules.IModule;
import org.deegree.igeo.modules.ModuleCapabilities;
import org.deegree.igeo.modules.ModuleException;
import org.deegree.igeo.modules.ActionDescription.ACTIONTYPE;
import org.deegree.igeo.views.DialogFactory;
/**
* The <code></code> class TODO add class documentation here.
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
*
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*
*/
public class WebServerModule<T> extends DefaultModule<T> {
static final ILogger LOG = LoggerFactory.getLogger( WebServerModule.class );
boolean run = false;
int port;
Map<String, Class<RequestHandler>> handler = new HashMap<String, Class<RequestHandler>>();
Map<String, Map<String, String>> handlerParameters = new HashMap<String, Map<String, String>>();
static {
ActionDescription ad1 = new ActionDescription( "start", "starts internal server to enable remote control",
null, "starts internal server", ACTIONTYPE.PushButton, null,
null );
ActionDescription ad2 = new ActionDescription( "stop", "stops internal server to avoid remote control", null,
"stops internal server", ACTIONTYPE.PushButton, null, null );
moduleCapabilities = new ModuleCapabilities( ad1, ad2 );
}
@Override
public void init( ModuleType moduleType, _ComponentPositionType componentPosition, ApplicationContainer<T> appCont,
IModule<T> parent, Map<String, String> initParams ) {
super.init( moduleType, componentPosition, appCont, parent, initParams );
String tmp = getInitParameter( "port" );
if ( tmp == null ) {
tmp = "80";
}
port = Integer.parseInt( tmp );
Map<String, String> params = getInitParameters();
for ( String function : params.keySet() ) {
if ( !function.equals( "port" ) && !function.equals( "autostart" ) && !function.contains( "." ) ) {
try {
handler.put( function, (Class<RequestHandler>) Class.forName( params.get( function ) ) );
} catch ( ClassNotFoundException e ) {
LOG.logError( e.getMessage(), e );
throw new ModuleException( e.getMessage() );
}
}
}
for ( String paramName : params.keySet() ) {
if ( paramName.contains( "." ) ) {
String functionName = paramName.split( "[.]" )[0];
String functionParam = paramName.split( "[.]" )[1];
Map<String, String> paramMap = handlerParameters.get( functionName );
if ( paramMap == null ) {
paramMap = new HashMap<String, String>();
handlerParameters.put( functionName, paramMap );
}
paramMap.put( functionParam.toUpperCase(), params.get( paramName ) );
}
}
if ( "true".equalsIgnoreCase( params.get( "autostart" ) ) ) {
try {
start();
} catch ( Throwable e ) {
LOG.logError( e.getMessage(), e );
throw new ModuleException( e.getMessage() );
}
}
}
/**
*
* @return <code>true</code> if internal server is running
*/
public boolean isServerRunning() {
return run;
}
/**
* starts the server
*/
public void start() {
run = true;
new Thread() {
@Override
public void run() {
ServerSocket socket;
try {
socket = new ServerSocket( port );
} catch ( IOException e ) {
e.printStackTrace();
return;
}
LOG.logInfo( "Waiting for client" );
while ( run ) {
Socket clientSocket;
try {
clientSocket = socket.accept();
} catch ( IOException e ) {
e.printStackTrace();
return;
}
final HTTPWorker httpWorker = new HTTPWorker( clientSocket );
( new Thread( httpWorker ) ).run();
}
}
}.start();
DialogFactory.openInformationDialog( appContainer.getViewPlatform(), null, Messages.get( "$MD11781" ),
Messages.get( "$MD11782" ) );
}
/**
* stops the server
*/
public void stop() {
run = false;
DialogFactory.openInformationDialog( appContainer.getViewPlatform(), null, Messages.get( "$MD11783" ),
Messages.get( "$MD11784" ) );
}
class HTTPWorker implements Runnable {
private Socket clientSocket;
public HTTPWorker( final Socket clientSocket ) {
this.clientSocket = clientSocket;
}
public void run() {
try {
InputStream is = this.clientSocket.getInputStream();
int k = 0;
int bytesToRead = 0;
while ( ( bytesToRead = is.available() ) == 0 && k++ < 10 ) {
Thread.sleep( 10 );
}
if ( bytesToRead == 0 ) {
// throw new IllegalArgumentException( "No request data found" );
return;
}
final byte[] barray = new byte[bytesToRead];
is.read( barray, 0, bytesToRead );
final String client_data = new String( barray );
final String regex = ".*GET (.*) HTTP.*";
final Pattern pattern = Pattern.compile( regex, Pattern.MULTILINE | Pattern.DOTALL );
final Matcher matcher = pattern.matcher( client_data );
if ( matcher.matches() ) {
final String requestedPage = matcher.group( 1 );
final Map<String, String> request = KVP2Map.toMap( requestedPage );
if ( request.get( "ACTION" ) == null ) {
final String msg = "parameter 'action' must be set";
final String content = "<html><head><title>400</title></head><body><h1>400 - Bad Request</h1>"
+ msg + "</body></html>";
final String data = buildHeader( content.length(), "400 - Bad Request" );
clientSocket.getOutputStream().write( data.getBytes() );
clientSocket.getOutputStream().write( content.getBytes() );
LOG.logError( msg );
} else if ( handler.containsKey( request.get( "ACTION" ) ) ) {
Class<RequestHandler> clzz = handler.get( request.get( "ACTION" ) );
RequestHandler obj = clzz.newInstance();
obj.init( handlerParameters.get( request.get( "ACTION" ) ) );
final String contentString = obj.perform( request, appContainer );
final String httpHeader = buildHeader( contentString.length(), "200 OK" );
clientSocket.getOutputStream().write( httpHeader.getBytes() );
clientSocket.getOutputStream().write( contentString.getBytes() );
} else {
final String msg = "action: " + request.get( "ACTION" ) + " not known by iGeoDesktop";
final String content = "<html><head><title>404</title></head><body><h1>404 - Not found</h1>"
+ msg + "</body></html>";
final String data = buildHeader( content.length(), "404 Not Found" );
clientSocket.getOutputStream().write( data.getBytes() );
clientSocket.getOutputStream().write( content.getBytes() );
LOG.logError( msg );
}
}
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
if ( "Application".equalsIgnoreCase( appContainer.getViewPlatform() ) ) {
DialogFactory.openWarningDialog( appContainer.getViewPlatform(), null, e.getMessage(), "error" );
}
}
}
private String buildHeader( final int stringLength, final String httpState ) {
return "HTTP/1.1 " + httpState + "\n" + "Content-Length: " + stringLength + "\n"
+ "Content-Type: text/html\n" + "Connection: close\n\n";
}
}
}