/**
Copyright (C) 2012 Delcyon, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.delcyon.capo.resourcemanager.remote;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import org.w3c.dom.Document;
import com.delcyon.capo.CapoApplication;
import com.delcyon.capo.controller.ControlElement;
import com.delcyon.capo.controller.server.ControllerClientRequestProcessor;
import com.delcyon.capo.protocol.server.AbstractClientRequestProcessor;
import com.delcyon.capo.protocol.server.ClientRequest;
import com.delcyon.capo.protocol.server.ClientRequestProcessor;
import com.delcyon.capo.protocol.server.ClientRequestProcessorSessionManager;
import com.delcyon.capo.protocol.server.ClientRequestXMLProcessor;
import com.delcyon.capo.resourcemanager.ResourceDescriptor;
import com.delcyon.capo.resourcemanager.ResourceParameter;
import com.delcyon.capo.resourcemanager.ResourceType;
import com.delcyon.capo.resourcemanager.ResourceURI;
import com.delcyon.capo.resourcemanager.remote.RemoteResourceDescriptorMessage.MessageType;
import com.delcyon.capo.resourcemanager.types.ContentMetaData;
import com.delcyon.capo.util.XMLSerializer;
import com.delcyon.capo.xml.XPath;
import com.delcyon.capo.xml.cdom.CDocument;
import com.delcyon.capo.xml.cdom.CElement;
import com.delcyon.capo.xml.cdom.VariableContainer;
import com.delcyon.capo.xml.dom.ResourceDeclarationElement;
/**
* @author jeremiah
*
*/
public class RemoteResourceDescriptorProxy extends AbstractClientRequestProcessor implements ResourceDescriptor,ClientRequestProcessor
{
private ControllerClientRequestProcessor controllerClientRequestProcessor;
private String lock = new String("lock");
private String inputStreamLock = new String("inputStreamLock");
private String outputStreamLock = new String("outputStreamLock");
private String sessionID = ClientRequestProcessorSessionManager.generateSessionID();
private VariableContainer variableContainer;
private InputStream inputStream;
private OutputStream outputStream;
private ResourceURI resourceURI = null;
private ResourceType resourceType;
private LifeCycle lifeCycle;
private boolean isStreamProcessor = false;
private ResourceDescriptor parentResourceDescriptor = null;
public RemoteResourceDescriptorProxy(ControllerClientRequestProcessor controllerClientRequestProcessor)
{
this.controllerClientRequestProcessor = controllerClientRequestProcessor;
ClientRequestProcessorSessionManager.registerClientRequestProcessor(this,sessionID);
}
@Override
public void setup(ResourceType resourceType, String resourceURI) throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
this.resourceURI = new ResourceURI(resourceURI);
this.resourceType = resourceType;
message.setMessageType(MessageType.SETUP);
message.setSessionID(sessionID);
message.setResourceURI(new ResourceURI(resourceURI));
message = sendResponse(message, MessageType.SETUP,false);
this.resourceURI = message.getResourceURI();
this.resourceType = message.getResourceType();
}
@Override
public void init(ResourceDeclarationElement declaringResourceElement, VariableContainer variableContainer, LifeCycle lifeCycle, boolean iterate, ResourceParameter... resourceParameters) throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
this.variableContainer = variableContainer;
message.setIterate(iterate);
message.setLifeCycle(lifeCycle);
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.INIT,false);
}
@Override
public State getResourceState() throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message = sendResponse(message, MessageType.GET_RESOURCE_STATE,false);
return message.getResourceState();
}
@Override
public void open(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.OPEN,false);
}
@Override
public ContentMetaData getResourceMetaData(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.GET_RESOURCE_METADATA,false);
return message.getResourceMetaData();
}
@Override
public ContentMetaData getContentMetaData(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.GET_CONTENT_METADATA,false);
return message.getContentMetaData();
}
@Override
public ContentMetaData getOutputMetaData(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.GET_OUTPUT_METADATA,false);
return message.getOutputMetaData();
}
@Override
public ResourceDescriptor getChildResourceDescriptor(ControlElement callingControlElement, String relativeURI) throws Exception
{
ContentMetaData contentMetaData = getResourceMetaData(null);
if ( contentMetaData.isContainer() == true)
{
List<ContentMetaData> childContentMetaDataList = getResourceMetaData(null).getContainedResources();
for (ContentMetaData childContentMetaData : childContentMetaDataList)
{
if(childContentMetaData.getResourceURI().getPath().endsWith(relativeURI))
{
ResourceDescriptor childResourceDescriptor = CapoApplication.getDataManager().getResourceDescriptor(callingControlElement, "remote:"+childContentMetaData.getResourceURI().getResourceURIString());
if (childResourceDescriptor != null)
{
childResourceDescriptor.setParentResourceDescriptor(this);
}
return childResourceDescriptor;
}
}
ResourceDescriptor childResourceDescriptor = CapoApplication.getDataManager().getResourceDescriptor(callingControlElement, "remote:"+getResourceURI().getResourceURIString()+"/"+relativeURI);
if (childResourceDescriptor != null)
{
childResourceDescriptor.setParentResourceDescriptor(this);
}
return childResourceDescriptor;
}
else
{
return null;
}
}
@Override
public ResourceDescriptor getParentResourceDescriptor() throws Exception
{
return this.parentResourceDescriptor ;
}
@Override
public void setParentResourceDescriptor(ResourceDescriptor parentResourceDescriptor) throws Exception
{
this.parentResourceDescriptor = parentResourceDescriptor;
}
@Override
public InputStream getInputStream(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
//unlock any existing threads
synchronized (inputStreamLock)
{
inputStreamLock.notify();
}
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.GET_INPUTSTREAM,true);
return this.inputStream;
}
@Override
public OutputStream getOutputStream(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
//unlock any existing threads
synchronized (outputStreamLock)
{
outputStreamLock.notify();
}
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.GET_OUTPUTSTREAM,true);
return this.outputStream;
}
@Override
public void addResourceParameters(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.ADD_RESOURCE_PARAMETERS,false);
}
@Override
public boolean isSupportedStreamFormat(StreamType streamType, StreamFormat streamFormat) throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setStreamType(streamType);
message.setStreamFormat(streamFormat);
message = sendResponse(message, MessageType.IS_STREAM_SUPPORETED_FORMAT,false);
return message.isSupportedStreamFormat();
}
@Override
public void close(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.CLOSE,false);
synchronized (inputStreamLock)
{
this.inputStreamLock.notify();
}
synchronized (outputStreamLock)
{
this.outputStreamLock.notify();
}
}
@Override
public void release(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.RELEASE,false);
synchronized (inputStreamLock)
{
this.inputStreamLock.notify();
}
synchronized (outputStreamLock)
{
this.outputStreamLock.notify();
}
ClientRequestProcessorSessionManager.removeClientRequestProcessor(getSessionId());
}
@Override
public void reset(State previousState) throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setPreviousState(previousState);
message = sendResponse(message, MessageType.RESET,false);
}
@Override
public void advanceState(State desiredState, VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message.setDesiredState(desiredState);
message = sendResponse(message, MessageType.ADVANCE_STATE,false);
}
@Override
public boolean next(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.STEP,false);
return message.isStepSuccess();
}
@Override
public CElement readXML(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.READ_XML,false);
return message.getXMLElement();
}
@Override
public void writeXML(VariableContainer variableContainer, CElement element, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message.setXMLElement(element);
message = sendResponse(message, MessageType.WRITE_XML,false);
}
@Override
public byte[] readBlock(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.READ_BLOCK,false);
return message.getBlock();
}
@Override
public void writeBlock(VariableContainer variableContainer, byte[] block, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message.setBlock(block);
message = sendResponse(message, MessageType.WRITE_BLOCK,false);
}
@Override
public void processInput(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.PROCESS_INPUT,false);
}
@Override
public void processOutput(VariableContainer variableContainer, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message = sendResponse(message, MessageType.PROCESS_OUTPUT,false);
}
@Override
public boolean performAction(VariableContainer variableContainer, Action action, ResourceParameter... resourceParameters) throws Exception
{
this.variableContainer = variableContainer;
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setResourceParameters(resourceParameters);
message.setAction(action);
message = sendResponse(message, MessageType.PERFORM_ACTION,false);
return message.getActionResult();
}
@Override
public LifeCycle getLifeCycle() throws Exception
{
if (this.lifeCycle == null)
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message = sendResponse(message, MessageType.GET_LIFCYCLE,false);
this.lifeCycle = message.getLifeCycle();
}
return this.lifeCycle;
}
@Override
public State getStreamState(StreamType streamType) throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setStreamType(streamType);
message = sendResponse(message, MessageType.GET_STREAM_STATE,false);
return message.getStreamState();
}
@Override
public StreamFormat[] getSupportedStreamFormats(StreamType streamType) throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setStreamType(streamType);
message = sendResponse(message, MessageType.GET_SUPPORTED_STREAM_FORMATS,false);
return message.getSupportedStreamFormats();
}
@Override
public StreamType[] getSupportedStreamTypes() throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message = sendResponse(message, MessageType.GET_SUPPORTED_STREAM_TYPES,false);
return message.getSupportedStreamTypes();
}
@Override
public boolean isSupportedAction(Action action) throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setAction(action);
message = sendResponse(message, MessageType.IS_SUPPORTED_ACTION,false);
return message.getActionResult();
}
@Override
public boolean isSupportedStreamType(StreamType streamType) throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage();
message.setStreamType(streamType);
message = sendResponse(message, MessageType.IS_SUPPORTED_STREAM_TYPE,false);
return message.isSupportedStreamType();
}
//===================CACHED RESULTS======================
@Override
public ResourceType getResourceType()
{
return this.resourceType;
}
@Override
public ResourceURI getResourceURI()
{
return this.resourceURI;
}
public void setResourceURI(ResourceURI resourceURI)
{
this.resourceURI = resourceURI;
}
/** returns last piece of URI **/
@Override
public String getLocalName()
{
String[] splitURI = this.resourceURI.getPath().split("/");
return splitURI[splitURI.length-1];
}
@Override
public boolean isRemoteResource()
{
return true;
}
//===================MESSAGING===========================
private RemoteResourceDescriptorMessage sendResponse(RemoteResourceDescriptorMessage message, MessageType messageType,boolean needLock) throws Exception
{
return sendResponse(message, messageType, needLock, this.controllerClientRequestProcessor.getClientRequestXMLProcessor());
}
private RemoteResourceDescriptorMessage sendResponse(RemoteResourceDescriptorMessage message, MessageType messageType,boolean needLock,ClientRequestXMLProcessor clientRequestXMLProcessor) throws Exception
{
message.setSessionID(sessionID);
message.setMessageType(messageType);
if(message.getResourceURI() == null)
{
message.setResourceURI(getResourceURI());
}
message.prepareResponse();
clientRequestXMLProcessor.writeResponse(message);
if (needLock)
{
synchronized (lock)
{
lock.wait(30000);
}
}
//wait for a message from the client
Document replyDocument = XPath.unwrapDocument(clientRequestXMLProcessor.readNextDocument(),true);
message = new RemoteResourceDescriptorMessage(replyDocument);
if (message.getMessageType() == MessageType.FAILURE)
{
if (message.getException() != null)
{
throw message.getException();
}
}
return message;
}
@Override
public String getSessionId()
{
return this.sessionID;
}
@Override
public void process(ClientRequest clientRequest) throws Exception
{
MessageType messageType = RemoteResourceRequest.getType(clientRequest);
//process the request
boolean useLock = false;
switch (messageType)
{
case GET_INPUTSTREAM:
this.inputStream = clientRequest.getInputStream();
useLock = true;
break;
case GET_OUTPUTSTREAM:
this.outputStream = clientRequest.getOutputStream();
useLock = true;
break;
case GET_VAR_VALUE:
processVarRequest(clientRequest);
break;
default:
throw new UnsupportedOperationException(messageType.toString());
}
//notify the other thread the the request has been processed
if (useLock == true)
{
synchronized (lock)
{
lock.notify();
}
}
//see if we need to wait until a lock is released, so any streams don't get closed
switch (messageType)
{
case GET_INPUTSTREAM:
synchronized (inputStreamLock)
{
this.inputStreamLock.wait();
}
break;
case GET_OUTPUTSTREAM:
synchronized (outputStreamLock)
{
this.outputStreamLock.wait();
}
break;
default:
break;
}
}
private void processVarRequest(ClientRequest clientRequest) throws Exception
{
RemoteResourceDescriptorMessage requestMessage = new RemoteResourceDescriptorMessage(XPath.unwrapDocument(XPath.unwrapDocument(clientRequest.getRequestDocument(), true),true));
String varName = requestMessage.getVarName();
RemoteResourceDescriptorMessage replyMessage = new RemoteResourceDescriptorMessage();
replyMessage.setVarName(varName);
if (variableContainer != null && varName != null)
{
replyMessage.setValue(variableContainer.getVarValue(varName));
}
replyMessage.setSessionID(sessionID);
replyMessage.setMessageType(MessageType.GET_VAR_VALUE);
if(replyMessage.getResourceURI() == null)
{
replyMessage.setResourceURI(getResourceURI());
}
CDocument replyDocument = CDocument.buildPath("RemoteResourceVarRequestReply");
XMLSerializer.export(replyDocument.getDocumentElement(), replyMessage);
clientRequest.getClientRequestXMLProcessor().getXmlStreamProcessor().writeDocument(replyDocument);
}
@Override
public void init(ClientRequestXMLProcessor clientRequestXMLProcessor, String sessionID, HashMap<String, String> sessionHashMap,String requestName) throws Exception
{
RemoteResourceDescriptorMessage message = new RemoteResourceDescriptorMessage(clientRequestXMLProcessor.getDocument());
switch (message.getMessageType())
{
case GET_INPUTSTREAM:
case GET_OUTPUTSTREAM:
isStreamProcessor = true;
break;
default:
isStreamProcessor = false;
break;
}
}
@Override
public boolean isStreamProcessor()
{
return isStreamProcessor;
}
@Override
public Document readNextDocument() throws Exception
{
throw new UnsupportedOperationException();
}
}