/**
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.controller;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;
import java.util.logging.Level;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.delcyon.capo.CapoApplication;
import com.delcyon.capo.controller.elements.GroupElement;
import com.delcyon.capo.controller.elements.ResourceControlElement;
import com.delcyon.capo.controller.server.ControllerClientRequestProcessor;
import com.delcyon.capo.resourcemanager.ResourceDescriptor;
import com.delcyon.capo.resourcemanager.ResourceDescriptor.LifeCycle;
import com.delcyon.capo.resourcemanager.ResourceDescriptor.State;
import com.delcyon.capo.resourcemanager.ResourceParameterBuilder;
import com.delcyon.capo.resourcemanager.ResourceURI;
import com.delcyon.capo.server.CapoServer;
import com.delcyon.capo.xml.XPath;
import com.delcyon.capo.xml.cdom.VariableContainer;
import com.delcyon.capo.xml.cdom.VariableProcessor;
import com.delcyon.capo.xml.dom.ResourceDocument;
import com.delcyon.capo.xml.dom.ResourceElement;
public class Group implements VariableContainer,VariableProcessor
{
private HashMap<String, String> entryHashMap = new HashMap<String, String>();
private HashMap<String, String> variableHashMap = new HashMap<String, String>();
private transient HashMap<String,ResourceControlElement> resourceElementHashMap = new HashMap<String, ResourceControlElement>();
private transient Vector<ResourceDescriptor> openResourceDescriptorVector = new Vector<ResourceDescriptor>();
private String groupName;
private transient Group parentGroup;
private GroupElement groupElement;
private ControllerClientRequestProcessor controllerClientRequestProcessor;
private VariableContainer variableContainer;
@SuppressWarnings("unused")
private Group() //needed for serialization
{
}
public Group(String groupName, Group parentGroup, GroupElement groupElement, ControllerClientRequestProcessor controllerClientRequestProcessor)
{
this.groupName = groupName;
this.parentGroup = parentGroup;
this.groupElement = groupElement;
this.controllerClientRequestProcessor = controllerClientRequestProcessor;
}
/**
* Get the name of this group
* @return
*/
public String getGroupName()
{
return groupName;
}
/**
* recurse up and get a unique path to this group
* This is really only used in logging in case of emergency
* @return
*/
public String getGroupPath()
{
//think about using saxon path function - this is here as a replacement to using path function()
//also path function only available in SaxonPE+
//thought about it, by using the group object, we get a tree through the call elements
//as opposed to the pure xpath which pays no attention to them.
if (parentGroup != null)
{
return parentGroup.getGroupPath() + ":" +getGroupName();
}
else
{
return getGroupName();
}
}
/**
* iterate through all of the attributes, and replace ant var declarations with their value
* @param node
* @return
*/
public Node replaceVarsInAttributeValues(Node node) throws Exception
{
NamedNodeMap namedNodeMap = node.getAttributes();
if (namedNodeMap == null)
{
return node;
}
for(int currentNode = 0; currentNode < namedNodeMap.getLength(); currentNode++)
{
Node attribute = namedNodeMap.item(currentNode);
attribute.setNodeValue(processVars(attribute.getNodeValue()));
}
return node;
}
/**
* Convience method that returns a processed varString
* @param varString
* @return
*/
public String processVars(String varString) throws Exception
{
StringBuffer stringBuffer = new StringBuffer(varString);
processVars(stringBuffer);
return stringBuffer.toString();
}
/**
* Check String for variables and replace them with the value of the var
* @param varStringBuffer
*/
public void processVars(StringBuffer varStringBuffer) throws Exception
{
while (varStringBuffer != null && varStringBuffer.toString().matches(".*\\$\\{.+}.*"))
{
CapoServer.logger.log(Level.FINE,"found var in '"+varStringBuffer+"'");
Stack<StringBuffer> stack = new Stack<StringBuffer>();
StringBuffer currentStringBuffer = new StringBuffer();
for (int index = 0; index < varStringBuffer.length(); index++)
{
if (varStringBuffer.charAt(index) == '$' && varStringBuffer.charAt(index+1) == '{')
{
stack.push(currentStringBuffer);
currentStringBuffer = new StringBuffer();
currentStringBuffer.append(varStringBuffer.charAt(index));
}
else if (varStringBuffer.charAt(index) == '}' && varStringBuffer.charAt(index-1) != '\\' && stack.empty() == false)
{
//pop, and evaluate
currentStringBuffer.append(varStringBuffer.charAt(index));
String varName = currentStringBuffer.toString().replaceFirst(".*\\$\\{(.+)}.*", "$1");
String value = getVarValue(varName);
if (value == null)
{
value = "";
CapoServer.logger.log(Level.WARNING,"var '"+varName+"' not found replaced with empty string");
}
currentStringBuffer = stack.pop();
currentStringBuffer.append(value);
}
else
{
currentStringBuffer.append(varStringBuffer.charAt(index));
}
}
varStringBuffer.replace(0, varStringBuffer.length(), currentStringBuffer.toString());
}
CapoServer.logger.log(Level.FINE,"final replacement = '"+varStringBuffer+"'");
}
/**
* check request
* check entries
* check variables
* @param varName
* @return
*/
public String getVarValue(String varName) throws Exception
{
if (controllerClientRequestProcessor != null && controllerClientRequestProcessor.getRequestHashMap().containsKey(varName))
{
return controllerClientRequestProcessor.getRequestHashMap().get(varName);
}
else if (controllerClientRequestProcessor != null && controllerClientRequestProcessor.getSessionHashMap() != null && controllerClientRequestProcessor.getSessionHashMap().containsKey(varName))
{
return controllerClientRequestProcessor.getSessionHashMap().get(varName);
}
else if (entryHashMap.containsKey(varName))
{
return entryHashMap.get(varName);
}
else if (variableHashMap.containsKey(varName))
{
return variableHashMap.get(varName);
}
else if (variableContainer != null) //this allows us to steer variable look up to some other hierarchy
{
return variableContainer.getVarValue(varName);
}
else if (parentGroup != null)
{
return parentGroup.getVarValue(varName);
}
else
{
return CapoApplication.getVariableValue(varName);
}
}
/**
* for debugging purposes, prints all of the variables
* @param printStream
*/
public void dumpVars(PrintStream printStream)
{
if (parentGroup != null)
{
parentGroup.dumpVars(printStream);
}
else
{
CapoApplication.dumpVars(printStream);
}
String spacing = "";
int tabCount = getGroupPath().split(":").length;
for(int depth = 0; depth < tabCount; depth++)
{
spacing += "\t";
}
printStream.println("\n"+spacing+"===================='"+getGroupName().toUpperCase()+"' GROUP VARS (path = "+getGroupPath()+")=====================");
printStream.println(spacing+"====================CONTAINER VARS=====================");
if (variableContainer != null) //this allows us to steer variable look up to some other hierarchy
{
printStream.println(spacing+variableContainer);
}
printStream.println(spacing+"====================LOCAL VARS=====================");
printStream.println(spacing+variableHashMap);
printStream.println(spacing+"====================ENTRY VARS=====================");
printStream.println(spacing+entryHashMap);
printStream.println(spacing+"====================SESSION VARS=====================");
if (controllerClientRequestProcessor != null && controllerClientRequestProcessor.getSessionHashMap() != null)
{
printStream.println(spacing+controllerClientRequestProcessor.getSessionHashMap());
}
printStream.println(spacing+"====================REQUEST VARS=====================");
if (controllerClientRequestProcessor != null)
{
printStream.println(spacing+controllerClientRequestProcessor.getRequestHashMap());
}
printStream.println(spacing+"====================END '"+getGroupName().toUpperCase()+"' GROUP VARS=====================\n");
}
public void set(String varName, String value)
{
if (varName.matches("\\$.*:.*"))
{
String[] scopedVariableArray = varName.split(":");
if (scopedVariableArray.length != 2)
{
CapoApplication.logger.log(Level.WARNING, "Improper format of scoped variable name. '"+varName+"' should be '$<group name>:<var name>'. Going to use local group.");
}
else
{
//strip off $ char
scopedVariableArray[0] = scopedVariableArray[0].replaceFirst("\\$", "");
Group currentGroup = this;
while (scopedVariableArray[0].equals(currentGroup.getGroupName()) == false)
{
currentGroup = currentGroup.parentGroup;
if (currentGroup == null)
{
break; //hit top of stack, so exit out
}
}
if (currentGroup != null)
{
currentGroup.variableHashMap.put(scopedVariableArray[1], value);
}
else
{
CapoApplication.logger.log(Level.WARNING, "No parent group named "+scopedVariableArray[0]+" was found. Using local group and full var name '"+varName+"'");
variableHashMap.put(varName,value);
}
}
}
else
{
variableHashMap.put(varName,value);
}
}
public GroupElement getGroupElement()
{
return groupElement;
}
public boolean containsLocalKey(String varName)
{
return (entryHashMap.containsKey(varName) || variableHashMap.containsKey(varName));
}
public String getLocalValue(String varName)
{
if (entryHashMap.containsKey(varName))
{
return entryHashMap.get(varName);
}
else
{
return variableHashMap.get(varName);
}
}
public HashMap<String, String> getVariableHashMap()
{
return variableHashMap;
}
public void setVariableHashMap(HashMap<String, String> variableHashMap)
{
this.variableHashMap = variableHashMap;
}
@Override
public VariableContainer getVariableContainer()
{
return this;
}
public void putResourceElement(ResourceControlElement resourceControlElement)
{
resourceElementHashMap.put(resourceControlElement.getName(), resourceControlElement);
}
public ResourceDescriptor openResourceDescriptor(ControlElement callingControlElement,String resourceURIString) throws Exception
{
ResourceDescriptor resourceDescriptor = getResourceDescriptor(callingControlElement, resourceURIString);
if (resourceDescriptor != null)
{
if (resourceDescriptor.getResourceState() == State.NONE)
{
resourceDescriptor.init(null,this,null,false, ResourceParameterBuilder.getResourceParameters(callingControlElement.getControlElementDeclaration()));
}
if (resourceDescriptor.getResourceState() != State.OPEN && resourceDescriptor.getResourceState() != State.STEPPING)
{
resourceDescriptor.open(this,ResourceParameterBuilder.getResourceParameters(callingControlElement.getControlElementDeclaration()));
}
}
return resourceDescriptor;
}
public ResourceDescriptor getResourceDescriptor(ControlElement callingControlElement,String resourceURIString) throws Exception
{
String scheme = ResourceURI.getScheme(resourceURIString);
String uriRemainder = ResourceURI.getSchemeSpecificPart(resourceURIString);
//figure out resource type
if (scheme != null && scheme.equals("resource"))
{
String resourceName = uriRemainder;
String[] parts = ResourceURI.getParts(uriRemainder);
if (resourceName != null)
{
if (resourceElementHashMap.containsKey(resourceName))
{
ResourceControlElement resourceControlElement = resourceElementHashMap.get(resourceName);
if(resourceControlElement.getResourceDocument() != null)
{
return ((ResourceElement) resourceControlElement.getResourceDocument().getDocumentElement()).getResourceDescriptor();
}
else
{
return resourceControlElement.getResourceDescriptor();
}
}
else if (resourceElementHashMap.containsKey(parts[0]))
{
ResourceControlElement resourceControlElement = resourceElementHashMap.get(parts[0]);
ResourceDocument resourceDocument = resourceControlElement.getResourceDocument();
Node node = XPath.selectSingleNode(resourceDocument, resourceURIString);
if (node != null && node instanceof com.delcyon.capo.xml.dom.ResourceElement)
{
return ((ResourceElement) node).getResourceDescriptor();
}
else
{
return null;
}
}
else if (parentGroup != null)
{
return parentGroup.getResourceDescriptor(callingControlElement, resourceURIString);
}
else
{
ResourceDescriptor resourceDescriptor = CapoApplication.getDataManager().getResourceDescriptor(callingControlElement, resourceURIString);
openResourceDescriptorVector.add(resourceDescriptor);
return resourceDescriptor;
}
}
else
{
throw new Exception("Invalid resource name '"+resourceURIString+"' @"+XPath.getXPath(callingControlElement.getControlElementDeclaration()));
}
}
else
{
ResourceDescriptor resourceDescriptor = CapoApplication.getDataManager().getResourceDescriptor(callingControlElement, resourceURIString);
openResourceDescriptorVector.add(resourceDescriptor);
return resourceDescriptor;
}
}
public void closeResourceDescriptors(LifeCycle lifeCycle) throws Exception
{
Set<Entry<String, ResourceControlElement>> resourceDescriptorEntrySet = resourceElementHashMap.entrySet();
for (Entry<String, ResourceControlElement> resourceElementEntry : resourceDescriptorEntrySet)
{
if (resourceElementEntry.getValue().getResourceDescriptor().getLifeCycle() == lifeCycle)
{
if (resourceElementEntry.getValue().getResourceDescriptor().getResourceState() != State.CLOSED)
{
resourceElementEntry.getValue().getResourceDescriptor().close(this);
}
}
}
for (ResourceDescriptor resourceDescriptor : openResourceDescriptorVector)
{
if (resourceDescriptor.getLifeCycle() == lifeCycle)
{
if (resourceDescriptor.getResourceState() != State.CLOSED)
{
resourceDescriptor.close(this);
}
}
}
}
public void destroy() throws Exception
{
//make sure everything is closed.. this is over kill
closeResourceDescriptors(LifeCycle.REF);
closeResourceDescriptors(LifeCycle.GROUP);
//call release on everything, this will close EVERYTHING including explicit resource descriptors
Set<Entry<String, ResourceControlElement>> resourceDescriptorEntrySet = resourceElementHashMap.entrySet();
for (Entry<String, ResourceControlElement> resourceElementEntry : resourceDescriptorEntrySet)
{
if (resourceElementEntry.getValue().getResourceDescriptor().getResourceState() != State.RELEASED)
{
resourceElementEntry.getValue().getResourceDescriptor().release(this);
}
resourceElementHashMap.remove(resourceElementEntry.getKey());
}
for(int index = 0; index < openResourceDescriptorVector.size(); index++)
{
ResourceDescriptor resourceDescriptor = openResourceDescriptorVector.get(index);
if (resourceDescriptor.getResourceState() != State.RELEASED)
{
resourceDescriptor.release(this);
}
openResourceDescriptorVector.remove(index);
index--;
}
//drop all of our pointers
resourceElementHashMap.clear();
openResourceDescriptorVector.clear();
}
/**
* this allows us to steer variable look up to some other hierarchy
* @param variableContainer
*/
public void setVariableContainer(VariableContainer variableContainer)
{
this.variableContainer = variableContainer;
}
public void setVars(Element controlElementDeclaration)
{
NamedNodeMap namedNodeMap = controlElementDeclaration.getAttributes();
if (namedNodeMap == null)
{
return;
}
for(int currentNode = 0; currentNode < namedNodeMap.getLength(); currentNode++)
{
Node attribute = namedNodeMap.item(currentNode);
set(attribute.getNodeName(), attribute.getNodeValue());
}
}
}