/*
* Copyright (C) 2000 - 2008 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://www.openbluedragon.org/
*/
package com.naryx.tagfusion.cfm.tag.awt;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Vector;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.nary.util.LogFile;
import com.naryx.tagfusion.cfm.engine.cfArgStructData;
import com.naryx.tagfusion.cfm.engine.cfComponentData;
import com.naryx.tagfusion.cfm.engine.cfData;
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.cfStructData;
import com.naryx.tagfusion.cfm.engine.cfcMethodData;
import com.naryx.tagfusion.cfm.engine.engineListener;
import com.naryx.tagfusion.cfm.wddx.cfWDDX;
import com.naryx.tagfusion.util.dummyServletRequest;
import com.naryx.tagfusion.util.dummyServletResponse;
import com.naryx.tagfusion.xmlConfig.xmlCFML;
public class MultiCastManager extends Object implements engineListener {
private String address;
private InetAddress bindAddress;
private InetAddress groupAddr;
private int port;
private TxdThread txdThread = null;
private RxdThread rxdThread = null;
private cfcRunnerThread cfcThread = null;
private String cfc, cfcMethod, webRoot;
private Vector outList, inList;
private boolean bRunning, bReceiving;
private int messages_rxd = 0, messages_txd = 0;
private long bytes_rxd = 0, bytes_txd = 0;
public MultiCastManager(String bindaddress, String address, int port) throws UnknownHostException{
cfEngine.registerEngineListener( this );
try {
bindAddress = InetAddress.getByName( bindaddress );
} catch (UnknownHostException e) {
try {
bindAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e1) {
log("-] MultiCastManager cannot start: " + e1.getMessage() );
return;
}
}
this.address = address;
this.port = port;
groupAddr = InetAddress.getByName( address );
bRunning = true;
bReceiving = false;
outList = new Vector();
inList = new Vector();
rxdThread = null;
txdThread = new TxdThread();
log("-] MultiCastManager started $Revision: 2374 $; bindAddr:" + bindAddress );
}
public void log(String logLine) {
LogFile.println("MultiCast", address +"#" + port + ": " + logLine);
}
public int getMessagesTxd(){
return messages_txd;
}
public int getMessagesRxd(){
return messages_rxd;
}
public long getBytesTxd(){
return bytes_txd;
}
public long getBytesRxd(){
return bytes_rxd;
}
public void broadcastData(String data) {
outList.add(data);
synchronized(txdThread){
txdThread.notify();
}
}
public void stopReceiving(){
if (rxdThread != null){
bReceiving = false;
rxdThread.shutdown();
rxdThread.interrupt();
rxdThread = null;
}
if ( cfcThread != null ){
cfcThread.interrupt();
try {
cfcThread.join( 1000 );
} catch (InterruptedException e) {
cfcThread.interrupt();
}
cfcThread = null;
}
}
public synchronized void registerReceiver(String cfc, String cfcMethod, String webServerRoot) {
this.cfc = cfc;
this.cfcMethod = cfcMethod;
this.webRoot = webServerRoot;
if (rxdThread == null)
rxdThread = new RxdThread();
}
public void engineAdminUpdate(xmlCFML config) {
}
public void engineShutdown() {
bRunning = false;
bReceiving = false;
if ( rxdThread != null ){
rxdThread.shutdown();
rxdThread.interrupt();
}
if ( txdThread != null ){
txdThread.interrupt();
}
if ( cfcThread != null ){
cfcThread.interrupt();
}
log("Shutdown");
}
// -------------------------------------
// -- Helper Threads
// -------------------------------------
class TxdThread extends Thread {
public TxdThread() {
super("MultiCastManager.TxdThread." + address + "#" + port);
setDaemon(true);
start();
}
public void run() {
while (bRunning) {
while (outList.size() == 0) {
try {
synchronized (this){
wait(1000);
}
} catch (InterruptedException e) {
log( "TxdThread.run.InterruptedException:" + e.getMessage() );
}
}
String outData = (String) outList.remove(0);
if ( outData != null )
sendData(outData);
}
}
private void sendData(String data) {
MulticastSocket msocket = null;
try {
msocket = new MulticastSocket( port );
msocket.setInterface( bindAddress );
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(), groupAddr, port);
msocket.send(packet);
messages_txd++;
bytes_txd += data.length();
} catch (SocketException e) {
log( "TxdThread.sendData.SocketException: " + e.getMessage() );
} catch (IOException e) {
log("TxdThread.sendData.IOException: " + e.getMessage());
} catch (SecurityException e) {
log("TxdThread.sendData.SecurityException: CFMULTICAST is not supported when the SecurityPermission UnmanagedCode attribute is not set.");
log("TxdThread.sendData.SecurityException: " + e.getMessage());
} finally {
try{ msocket.close(); }catch(Exception ignore){}
}
}
}
// ----------------------------------------------------
class RxdThread extends Thread {
private MulticastSocket msocket;
public RxdThread() {
super("MultiCastManager.RxdThread." + address + "#" + port);
setDaemon(true);
bReceiving = true;
try {
msocket = new MulticastSocket( port );
msocket.setInterface( bindAddress );
msocket.joinGroup(groupAddr);
start();
cfcThread = new cfcRunnerThread();
} catch (IOException e) {
log( "RxdThread.IOException:" + e.getMessage() );
}
}
public void shutdown(){
bReceiving = false;
msocket.close();
}
public void run() {
byte inBuffer[] = new byte[64000];
while (bReceiving) {
try {
DatagramPacket packet = new DatagramPacket(inBuffer, inBuffer.length);
//- Wait for the datagram packet
msocket.receive(packet);
messages_rxd++;
bytes_rxd += packet.getLength();
//- Queue the message in
HashMap h = new HashMap();
h.put( "data", new String(inBuffer, 0, packet.getLength() ) );
h.put( "from", packet.getAddress().getHostAddress() );
inList.add( h );
synchronized (inList){
inList.notify();
}
log( "Mess In: From=" + packet.getAddress() + "; Size=" + packet.getLength() );
} catch (IOException e) {
if ( bReceiving )
log( "RxdThread.run.IOException:" + e.getMessage() );
}
}
msocket.close();
}
}
// ----------------------------------------------------
class cfcRunnerThread extends Thread {
public cfcRunnerThread(){
super("MultiCastManager.cfcRunnerThread." + cfc + "." + cfcMethod );
setDaemon(true);
start();
}
public void run(){
while (bReceiving) {
while (inList.size() == 0) {
try {
synchronized (inList){
inList.wait(1000);
}
} catch (InterruptedException e) {
if ( bReceiving )
log( "cfcRunnerThread.run.InterruptedException:" + e.getMessage() );
return;
}
}
HashMap cfcData = (HashMap)inList.remove(0);
if ( cfcData != null )
runCFC( cfcData );
}
}
private void runCFC(HashMap cfcData ){
cfSession tmpSession = null;
try {
long startTime = System.currentTimeMillis();
HttpServletRequest REQ = new dummyServletRequest(webRoot);
HttpServletResponse RES = new dummyServletResponse();
tmpSession = new cfSession(REQ, RES, cfEngine.thisServletContext);
cfStructData mess = new cfStructData();
//- set the data
String data = (String)cfcData.get("data");
if ( data.startsWith("<wddxPacket version='1.0'><header></header><data>") ){
mess.setData("cfmldata", cfWDDX.wddx2Cfml( data, tmpSession) );
}else{
mess.setData("textdata", new cfStringData( data ) );
}
//- set the from field
mess.setData( "from", new cfStringData( (String)cfcData.get("from") ) );
//- Call the CFC
cfArgStructData args = new cfArgStructData();
args.setData( "data", mess );
cfComponentData component = new cfComponentData(tmpSession, cfc);
cfcMethodData invocationData = new cfcMethodData(tmpSession, cfcMethod, args);
cfData returnData = component.invokeComponentFunction(tmpSession, invocationData);
if ( returnData.getDataType() == cfData.CFSTRINGDATA || returnData.getDataType() == cfData.CFNUMBERDATA
|| returnData.getDataType() == cfData.CFBOOLEANDATA || returnData.getDataType() == cfData.CFDATEDATA ){
log( "cfcRan: Time=" + (System.currentTimeMillis()-startTime) + "ms. Returned " + returnData.getString() );
}else{
log( "cfcRan: Time=" + (System.currentTimeMillis()-startTime) + "ms" );
}
} catch (Exception E) {
log( cfc + "." + cfcMethod + ".runCFC: " + E.getMessage());
} finally {
// Make sure per request connections are closed (bug NA#3174)
if ( tmpSession != null ) tmpSession.sessionEnd();
}
}
}
}