/*
* Copyright (C) 2000 - 2011 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://openbd.org/
* $Id: SocketServerDataFactory.java 1903 2011-12-30 20:46:20Z alan $
*/
package com.bluedragon.net.socket;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.bluedragon.plugin.ObjectCFC;
import com.bluedragon.plugin.PluginManager;
import com.naryx.tagfusion.cfm.application.cfAPPLICATION;
import com.naryx.tagfusion.cfm.application.cfApplicationData;
import com.naryx.tagfusion.cfm.engine.cfArrayData;
import com.naryx.tagfusion.cfm.engine.cfEngine;
import com.naryx.tagfusion.cfm.engine.cfSession;
import com.naryx.tagfusion.cfm.engine.cfStringData;
import com.naryx.tagfusion.cfm.engine.cfmRunTimeException;
import com.naryx.tagfusion.cfm.engine.variableStore;
/**
* Manages the Server Sockets for the ServerSocketXXX() functionality.
*
* This is a singleton class that wraps the threads for handling the servers; they are all destroyed on the close down of the main server
*
*/
public class SocketServerDataFactory {
public static SocketServerDataFactory thisInst = new SocketServerDataFactory();
private Map<Integer, SocketServerAccept> servers = null;
private SocketServerDataFactory(){}
public void close(){
if ( servers == null )
return;
Iterator<SocketServerAccept> it = servers.values().iterator();
while ( it.hasNext() )
it.next().close();
}
public synchronized boolean addServer( String ip, int port, String cfc, String appname ) throws Exception{
if ( servers == null )
servers = new HashMap<Integer, SocketServerAccept>();
if ( servers.containsKey(port) )
return false;
// Test the CFC is ok; this will throw an error if wrong
PluginManager.getPlugInManager().createCFC( PluginManager.getPlugInManager().createBlankSession(), cfc );
// All is well, so let us move forward
servers.put( port, new SocketServerAccept(ip,port,cfc,appname) );
return true;
}
public synchronized void closeServer( int port ){
if ( servers == null )
return;
SocketServerAccept ssa = servers.get(port);
if (ssa != null){
ssa.close();
servers.remove(port);
}
}
public synchronized cfArrayData getClients( int port ) throws cfmRunTimeException{
if ( servers == null )
return null;
SocketServerAccept ssa = servers.get(port);
if (ssa != null){
return ssa.getClients();
}else
return null;
}
/**
* This is the thread that runs in response
*
*/
private class SocketServerAccept extends Thread {
private ServerSocket serversocket;
private String cfc, appname;
private List<remoteclient> connectedClients;
SocketServerAccept( String ip, int port, String cfc, String appname ) throws IOException{
super("SocketServerAccept#" + port );
this.cfc = cfc;
this.appname = appname;
connectedClients = new ArrayList<remoteclient>();
if ( ip == null )
serversocket = new ServerSocket( port, 20 );
else
serversocket = new ServerSocket( port, 20, InetAddress.getByName(ip) );
start();
}
public cfArrayData getClients() throws cfmRunTimeException{
cfArrayData array = cfArrayData.createArray(1);
Iterator<remoteclient> it = connectedClients.iterator();
while ( it.hasNext() ){
array.addElement( it.next().getCFC().getComponentCFC() );
}
return array;
}
public void close(){
interrupt();
Iterator<remoteclient> it = connectedClients.iterator();
while ( it.hasNext() ){
it.next().close();
}
}
public void disconnectClient( remoteclient client ){
connectedClients.remove(client);
}
public void run(){
cfEngine.log("Server #" + serversocket.getLocalPort() + ": Started");
for (;;){
Socket socket = null;
try {
socket = serversocket.accept();
} catch (IOException e) {
break;
}
startCFC( socket );
}
// this server has stopped
servers.remove( serversocket.getLocalPort() );
cfEngine.log("Server #" + serversocket.getLocalPort() + ": Stopped");
}
private void startCFC(Socket socket){
try {
SocketData socketdata = new SocketData(socket, true);
cfSession tmpSession = PluginManager.getPlugInManager().createBlankSession();
if ( appname != null ){
cfApplicationData appData = cfAPPLICATION.getAppManager().getAppData( tmpSession, appname );
tmpSession.setQualifiedData( variableStore.APPLICATION_SCOPE, appData );
}
ObjectCFC cfcObj = PluginManager.getPlugInManager().createCFC( tmpSession, cfc );
connectedClients.add( new remoteclient(this, tmpSession, cfcObj, socketdata ) );
} catch (Exception e) {
cfEngine.log("SocketServer.startCFC(" + cfc + ") " + e.getMessage() );
}
}
}
/**
* Handles the remote client
*
* @author alan
*
*/
class remoteclient extends Thread {
private SocketServerAccept server;
private cfSession session;
private ObjectCFC cfcobj;
private SocketData socketdata;
public remoteclient(SocketServerAccept server, cfSession tmpSession, ObjectCFC cfcObj, SocketData socketdata ){
this.server = server;
this.session = tmpSession;
this.cfcobj = cfcObj;
this.socketdata = socketdata;
start();
}
public ObjectCFC getCFC(){
return cfcobj;
}
public void close(){
socketdata.disconnect();
interrupt();
}
public void run(){
// Call the CFC
onConnect();
while ( socketdata.isConnected() ){
String lineIn;
try {
lineIn = socketdata.readLine( 30000 );
if ( lineIn == null )
continue;
onReadLine(lineIn);
} catch (Exception e) {
if ( "Read timed out".equals( e.getMessage() ) )
continue;
socketdata.disconnect();
break;
}
}
// this one is finished with
onDisconnect();
server.disconnectClient(this);
}
private synchronized void onReadLine(String line){
cfcobj.clearArguments();
cfcobj.addArgument( "socketdata", socketdata );
cfcobj.addArgument("line", new cfStringData(line) );
try {
cfcobj.runMethodReturnBoolean( session, "onreadline" );
} catch (Exception e) {
cfEngine.log("SocketServer.RemoteClient.onReadLine(): " + e.getMessage() );
}
}
private synchronized void onConnect(){
cfcobj.clearArguments();
cfcobj.addArgument( "socketdata", socketdata );
try {
cfcobj.runMethodReturnBoolean( session, "onconnect" );
} catch (Exception e) {
cfEngine.log("SocketServer.RemoteClient.onConnect(): " + e.getMessage() );
}
}
private synchronized void onDisconnect(){
cfcobj.clearArguments();
cfcobj.addArgument( "socketdata", socketdata );
try {
cfcobj.runMethodReturnBoolean( session, "ondisconnect" );
} catch (Exception e) {
cfEngine.log("SocketServer.RemoteClient.onDisconnect(): " + e.getMessage() );
}
}
}
}