/*
Copyright (c) 2003-2009 ITerative Consulting Pty Ltd. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package DDEProject;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.TooManyListenersException;
import org.apache.log4j.Logger;
import Framework.AsyncStarter;
import Framework.ErrorMgr;
import Framework.EventManager;
import Framework.ParameterHolder;
import Framework.TextData;
import Framework.UsageException;
import com.neva.DdeClient;
import com.neva.DdeClientTransactionEvent;
import com.neva.DdeException;
/**
*
* A DDEConversation object is used by a client application to establish the connection to a DDE server. All requests for data are invoked by this object.
*
*/
public class DDEConversation extends DdeClient implements DDEObject{
public static class AsyncRunner extends AsyncStarter {
public Thread requestLinkStart(final DDEConversation ddeConversation, final TextData item, final boolean isHot) {
return super.startTask(ddeConversation, "DDEConversation.requestLinkStart",
new AsyncStarter.AsyncInvoker() {
@Override
public void runWithNoReturn() {
ddeConversation.requestLinkStart(item, isHot);
}
});
}
public Thread requestLinkStart(final DDEConversation ddeConversation, final String item, final boolean isHot) {
return super.startTask(ddeConversation, "DDEConversation.requestLinkStart",
new AsyncStarter.AsyncInvoker() {
@Override
public void runWithNoReturn() {
ddeConversation.requestLinkStart(item, isHot);
}
});
}
}
private static final int WAIT = 200;
private static final int EXEC = 1;
private static final int GET_ITEM = 2;
private static final int LINK_END = 3;
private static final int LINK_START = 4;
private static final int PUT_ITEM = 5;
protected static Logger _log = Logger.getLogger(DDEConversation.class);
protected TextData application;
protected HashMap<Integer, AsyncRequest> txIDs;
protected HashMap<String, Boolean> links;
public DDEConversation() {
super();
this.txIDs = new HashMap<Integer, AsyncRequest>();
this.links = new HashMap<String, Boolean>();
try {
this.addDdeClientTransactionEventListener(new com.neva.DdeClientTransactionEventAdaptor() {
@Override
public void onAsyncComplete(com.neva.DdeClientTransactionEvent event){
int txID = event.getAsyncTransId();
AsyncRequest request;
synchronized (DDEConversation.this.txIDs){
request = DDEConversation.this.txIDs.get(txID);
DDEConversation.this.txIDs.remove(txID);
}
Hashtable<String, ParameterHolder> qq_Params = new Hashtable<String, ParameterHolder>();
switch (request.getType()){
case EXEC:
qq_Params.put( "Command", new ParameterHolder(new TextData(request.getValue())));
qq_Params.put( "IsOkay", new ParameterHolder(event.getAsyncTransSuccess()));
qq_Params.put( "ReturnCode", new ParameterHolder(0));
qq_Params.put( "IsBusy", new ParameterHolder(false));
EventManager.postEvent(DDEConversation.this, "ExecComplete", qq_Params);
break;
case GET_ITEM:
qq_Params.put( "Item", new ParameterHolder(new TextData(request.getValue())));
qq_Params.put( "IsAvailable", new ParameterHolder(event.getAsyncTransSuccess()));
qq_Params.put( "Text", new ParameterHolder(new TextData(new String(event.getDdeData()).trim())));
qq_Params.put( "IsBusy", new ParameterHolder(false));
qq_Params.put( "ReturnCode", new ParameterHolder(0));
EventManager.postEvent(DDEConversation.this, "GetItemComplete", qq_Params);
break;
case LINK_END:
break;
case LINK_START:
break;
case PUT_ITEM:
qq_Params.put( "Item", new ParameterHolder(new TextData(request.getValue())));
qq_Params.put( "IsOkay", new ParameterHolder(event.getAsyncTransSuccess()));
qq_Params.put( "IsBusy", new ParameterHolder(false));
qq_Params.put( "ReturnCode", new ParameterHolder(0));
EventManager.postEvent(DDEConversation.this, "PutItemComplete", qq_Params);
break;
}
}
@Override
public void onDisconnect(com.neva.DdeClientTransactionEvent event) {
EventManager.postEvent(DDEConversation.this, "Disconmected");
}
@Override
public void onAdviseData(DdeClientTransactionEvent event) {
String item = event.getItem();
boolean isHot;
synchronized (DDEConversation.this.links){
isHot = DDEConversation.this.links.get(item);
}
Hashtable<String, ParameterHolder> qq_Params = new Hashtable<String, ParameterHolder>();
if (isHot){
qq_Params.put( "Item", new ParameterHolder(new TextData(item)));
qq_Params.put( "Text", new ParameterHolder(new TextData(new String(event.getDdeData()).trim())));
EventManager.postEvent(DDEConversation.this, "HotLinkDataArrived", qq_Params);
} else {
qq_Params.put( "Item", new ParameterHolder(new TextData(item)));
EventManager.postEvent(DDEConversation.this, "WarmLinkDataAvailable", qq_Params);
}
// qq_Params.put( "isActive", new ParameterHolder(true));
// qq_Params.put( "item", new ParameterHolder(""));
// EventManager.postEvent(DDEConversation.this, "LinkStatus", qq_Params);
}
@Override
public void onException(DdeClientTransactionEvent event) {
}
});
} catch (TooManyListenersException e) {
UsageException errorVar = new UsageException("Cannot create DDEConversation", e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
/**
* Establishes connection to the DDE server.
* @param application
* @param topic
* @param autoLaunch this parameter is not implemented
*/
public void initiateConnection(String application, String topic, boolean autoLaunch){
try {
this.setApplication(new TextData(application));
this.connect(application, topic);
} catch (DdeException e) {
UsageException errorVar = new UsageException("Cannot connect to DDE application: " + application, e);
ErrorMgr.addError(errorVar);
throw errorVar;
} catch (InterruptedException e) {
UsageException errorVar = new UsageException("Cannot connect to DDE application: " + application, e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
public void initiateConnection(String application, TextData topic, boolean autoLaunch){
this.initiateConnection(application, topic.toString(), autoLaunch);
}
public void initiateConnection(TextData application, String topic, boolean autoLaunch){
this.initiateConnection(application.toString(), topic, autoLaunch);
}
public void initiateConnection(TextData application, TextData topic, boolean autoLaunch){
this.initiateConnection(application.toString(), topic.toString(), autoLaunch);
}
/**
* Executes the specified command.
* @param command
*/
public void requestExec(String command){
try {
int txID = executeAsync(command);
AsyncRequest request = new AsyncRequest(command, EXEC, txID, false);
synchronized (this.txIDs) {
this.txIDs.put(txID, request);
}
} catch (DdeException e) {
UsageException errorVar = new UsageException("Error executing DDE command: " + command, e);
ErrorMgr.addError(errorVar);
throw errorVar;
} catch (InterruptedException e) {
UsageException errorVar = new UsageException("Error executing DDE command: " + command, e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
public void requestExec(TextData command) {
this.requestExec(command.toString());
}
/**
* The RequestGetItem method is used to retrieve data from the DDE server.
* @param item
*/
public void requestGetItem(String item){
try {
int txID = this.requestAsync(item, com.neva.DdeUtil.CF_TEXT);
AsyncRequest request = new AsyncRequest(item, GET_ITEM, txID, false);
synchronized (this.txIDs) {
this.txIDs.put(txID, request);
}
} catch (DdeException e) {
UsageException errorVar = new UsageException("Error in DDE request for item: " + item, e);
ErrorMgr.addError(errorVar);
throw errorVar;
} catch (InterruptedException e) {
UsageException errorVar = new UsageException("Error in DDE request for item: " + item, e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
public void requestGetItem(TextData item){
this.requestGetItem(item.toString());
}
/**
* The RequestLinkEnd method terminates a hot or warm link.
* @param item
*/
public void requestLinkEnd(String item){
try {
this.stopAdvise(item, com.neva.DdeUtil.CF_TEXT, WAIT);
synchronized (this.links) {
this.links.remove(item);
}
} catch (DdeException e) {
UsageException errorVar = new UsageException("Error in DDE LinkEnd for item: " + item, e);
ErrorMgr.addError(errorVar);
throw errorVar;
} catch (InterruptedException e) {
UsageException errorVar = new UsageException("Error in DDE LinkEnd for item: " + item, e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
public void requestLinkEnd(TextData item){
this.requestLinkEnd(item.toString());
}
/**
* The RequestLinkStart method initiates a hot or warm link.
* @param item
* @param isHot
*/
public void requestLinkStart(String item, boolean isHot){
try {
this.startAdvise(item, com.neva.DdeUtil.CF_TEXT, isHot, WAIT);
synchronized (this.links) {
this.links.put(item, isHot);
}
} catch (DdeException e) {
UsageException errorVar = new UsageException("Error in DDE LinkStart for item: " + item, e);
ErrorMgr.addError(errorVar);
throw errorVar;
} catch (InterruptedException e) {
UsageException errorVar = new UsageException("Error in DDE LinkStart for item: " + item, e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
public void requestLinkStart(TextData item, boolean isHot){
this.requestLinkStart(item.toString(), isHot);
}
/**
* The RequestPutItem method allows the DDE client to update data in the DDE server.
* @param item
* @param text
*/
public void requestPutItem(String item, String text){
try {
int txID = this.pokeAsync(item, getByteArrayWithNull(text), com.neva.DdeUtil.CF_TEXT);
AsyncRequest request = new AsyncRequest(item, PUT_ITEM, txID, false);
synchronized (this.txIDs) {
this.txIDs.put(txID, request);
}
} catch (DdeException e) {
UsageException errorVar = new UsageException("Error in DDE PutItem for item: " + item + " with value: " + text, e);
ErrorMgr.addError(errorVar);
throw errorVar;
} catch (InterruptedException e) {
UsageException errorVar = new UsageException("Error in DDE PutItem for item: " + item + " with value: " + text, e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
public void requestPutItem(TextData item, String text) {
this.requestPutItem(item.toString(), text);
}
public void requestPutItem(String item, TextData text) {
this.requestPutItem(item, text.toString());
}
public void requestPutItem(TextData item, TextData text) {
this.requestPutItem(item.toString(), text.toString());
}
/**
* The TerminateConnection method explicitly terminates the connection to the DDE server.
*/
public void terminateConnection( ){
try {
this.disconnect();
} catch (DdeException e) {
UsageException errorVar = new UsageException("Error disconnecting from DDE application: " + getApplication(), e);
ErrorMgr.addError(errorVar);
throw errorVar;
} catch (InterruptedException e) {
UsageException errorVar = new UsageException("Error disconnecting from DDE application: " + getApplication(), e);
ErrorMgr.addError(errorVar);
throw errorVar;
}
}
public TextData getApplication() {
return this.application;
}
public void setApplication(TextData application) {
this.application = application;
}
public String toString(){
return "DDEConversation [" + application + "," + topic + "]";
}
@Override
protected void finalize() throws Throwable {
super.finalize();
this.links.clear();
this.txIDs.clear();
}
/**
* returns an null terminated string as a Byte array
* @param source
*/
private byte[] getByteArrayWithNull(String source){
byte[] result = new byte[source.length() + 1];
System.arraycopy(source.getBytes(), 0, result, 0, source.length());
result[result.length-1] = 0;
return result;
}
/**
* this in an internal class used to store the type and value of an async request/command
*
*/
protected class AsyncRequest {
String value;
int type;
int ixID;
boolean isActive;
public AsyncRequest(String value, int type, int ixID, boolean isActive) {
super();
this.value = value;
this.type = type;
this.ixID = ixID;
this.isActive = isActive;
}
public String getValue() {
return value;
}
public int getType() {
return type;
}
public int getIxID() {
return ixID;
}
}
}