/** 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.util.HashMap; import java.util.Set; 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 org.w3c.dom.NodeList; import com.delcyon.capo.CapoApplication; import com.delcyon.capo.ContextThread; import com.delcyon.capo.annotations.ControlNamespaceURI; import com.delcyon.capo.controller.client.ClientSideControl; import com.delcyon.capo.controller.client.ServerControllerResponse; import com.delcyon.capo.controller.elements.DebugElement; import com.delcyon.capo.controller.elements.GroupElement; import com.delcyon.capo.controller.server.ControllerClientRequestProcessor; import com.delcyon.capo.controller.server.ServerSideControl; import com.delcyon.capo.exceptions.MissingAttributeException; import com.delcyon.capo.modules.ModuleProvider; import com.delcyon.capo.resourcemanager.ResourceDescriptor.LifeCycle; import com.delcyon.capo.xml.XPath; /** * @author jeremiah * */ @SuppressWarnings("unchecked") @ControlNamespaceURI(URIList={CapoApplication.CAPO_NAMESPACE_URI,CapoApplication.SERVER_NAMESPACE_URI,CapoApplication.CLIENT_NAMESPACE_URI,CapoApplication.RESOURCE_NAMESPACE_URI}) public abstract class AbstractControl implements ServerSideControl { private static HashMap<String, Class> controlNamespaceURIHashMap = null; static { controlNamespaceURIHashMap = new HashMap<String, Class>(); Set<String> controlNamespaceURISet = CapoApplication.getAnnotationMap().get(ControlNamespaceURI.class.getCanonicalName()); for (String className : controlNamespaceURISet) { try { Class controlNamespaceURIClass = Class.forName(className); ControlNamespaceURI controlNamespaceURI = (ControlNamespaceURI) controlNamespaceURIClass.getAnnotation(ControlNamespaceURI.class); String[] uriList = controlNamespaceURI.URIList(); for (String uri : uriList) { controlNamespaceURIHashMap.put(uri, controlNamespaceURIClass); CapoApplication.logger.log(Level.CONFIG, "Loaded controlNamespaceURI '"+uri+"' from "+className); } } catch (ClassNotFoundException classNotFoundException) { CapoApplication.logger.log(Level.WARNING, "Error getting controlNamespaceURI",classNotFoundException); } } } private static HashMap<String, Class> controlElementHashMap = null; static { controlElementHashMap = new HashMap<String, Class>(); Set<String> controlElementProviderSet = CapoApplication.getAnnotationMap().get(ControlElementProvider.class.getCanonicalName()); for (String className : controlElementProviderSet) { try { Class controlElementProviderClass = Class.forName(className); ControlElementProvider controlElementProvider = (ControlElementProvider) controlElementProviderClass.getAnnotation(ControlElementProvider.class); controlElementHashMap.put(controlElementProvider.name(), controlElementProviderClass); CapoApplication.logger.log(Level.CONFIG, "Loaded ControlElementProvider <"+controlElementProvider.name()+"/> from "+className); } catch (ClassNotFoundException classNotFoundException) { CapoApplication.logger.log(Level.WARNING, "Error getting ControlElement providers",classNotFoundException); } } } public static ControlElement getControlElementInstanceForLocalName(String localName) throws Exception { Class controlElementClass = controlElementHashMap.get(localName); if (controlElementClass != null) { return (ControlElement) controlElementClass.newInstance(); } else { return null; } } public static boolean isControlNamespace(String namespaceURI) { if (namespaceURI == null) { return false; } return controlNamespaceURIHashMap.containsKey(namespaceURI); } private Group parentGroup; private transient Element controlElementDeclaration; private transient Element originalControlElementDeclaration; private ControllerClientRequestProcessor controllerClientRequestProcessor; private transient ControlElement parentControlElement; public void processChildren(NodeList children, Group parentGroup, ControlElement parentControl, ControllerClientRequestProcessor controllerClientRequestProcessor) throws Exception { processAbstractChildren(children, parentGroup, parentControl, controllerClientRequestProcessor); } public void processChildren(NodeList children, Group parentGroup, ControlElement parentControl, ServerControllerResponse serverControllerResponse) throws Exception { processAbstractChildren(children, parentGroup, parentControl, serverControllerResponse); } private void processAbstractChildren(NodeList children, Group parentGroup, ControlElement parentControl, Object clientRequestOrServerResponse) throws Exception { Object originalContext = getContext(); for (int currentNode = 0; currentNode < children.getLength(); currentNode++) { Node node = children.item(currentNode); if (node.getNodeType() != Node.ELEMENT_NODE) { continue; } if(isControlNamespace(node.getNamespaceURI()) == false) { continue; } Element controlElementDeclaration = (Element) node; if (controlElementDeclaration.hasAttribute("DoNotProcess")) { continue; } //skip ControllerRequest element as it's just here for xpath processing, but not control processing. if (controlElementDeclaration.getLocalName().equals("ControllerRequest")) { continue; } ControlElement controlElement = getControlElementInstanceForLocalName(controlElementDeclaration.getLocalName()); if (controlElement == null) { //see if we are a module reference Element moduleElement = ModuleProvider.getModuleElement(controlElementDeclaration.getLocalName()); if (moduleElement != null) { //verify name attribute on module element if (moduleElement.hasAttribute("name") == false) { moduleElement.setAttribute("name", controlElementDeclaration.getLocalName()); } CapoApplication.logger.log(Level.FINE, "found '"+controlElementDeclaration.getLocalName()+"' module."); controlElement = getControlElementInstanceForLocalName(moduleElement.getLocalName()); if (controlElement != null) { NamedNodeMap attributeNodeMap = controlElementDeclaration.getAttributes(); for(int index = 0; index < attributeNodeMap.getLength(); index++) { moduleElement.setAttribute(attributeNodeMap.item(index).getLocalName(), attributeNodeMap.item(index).getNodeValue()); } NodeList childNodes = controlElementDeclaration.getChildNodes(); boolean isFirstChild = true; Element moduleDataElement = moduleElement.getOwnerDocument().createElement("server:moduleData"); moduleDataElement.setAttribute("DoNotProcess", "true"); for(int index = 0; index < childNodes.getLength(); index++) { Node childNode = childNodes.item(index); if (childNode instanceof Element) { if (isFirstChild) { moduleElement.appendChild(moduleDataElement); } moduleDataElement.appendChild(moduleElement.getOwnerDocument().importNode(childNode, true)); } } controlElementDeclaration = moduleElement; //XPath.dumpNode(controlElementDeclaration, System.err); } } } if (controlElement != null) { setContext(controlElement); try { String name = controlElementDeclaration.getAttribute("name"); if (name != null && name.isEmpty() == false) { CapoApplication.logger.log(Level.FINE, "Setting "+controlElementDeclaration.getLocalName()+"."+name+" to "+XPath.getXPath(controlElementDeclaration)); parentGroup.set(controlElementDeclaration.getLocalName()+"."+name, XPath.getXPath(controlElementDeclaration)); } if (clientRequestOrServerResponse instanceof ControllerClientRequestProcessor) { ((ServerSideControl) controlElement).init(controlElementDeclaration,this,parentGroup,(ControllerClientRequestProcessor) clientRequestOrServerResponse); Enum[] missingAttributes = controlElement.getMissingAttributes(); if (missingAttributes.length > 0) { throw new MissingAttributeException(missingAttributes,controlElementDeclaration); } ((ServerSideControl) controlElement).processServerSideElement(); } else if (clientRequestOrServerResponse instanceof ServerControllerResponse) { ((ClientSideControl) controlElement).init(controlElementDeclaration,this,parentGroup,(ServerControllerResponse) clientRequestOrServerResponse); Enum[] missingAttributes = controlElement.getMissingAttributes(); if (missingAttributes.length > 0) { throw new MissingAttributeException(missingAttributes,controlElementDeclaration); } ((ClientSideControl) controlElement).processClientSideElement(); } if (controlElement instanceof AbstractControl) { DebugElement.debugElement((AbstractControl) controlElement); } if (parentGroup != null) { parentGroup.closeResourceDescriptors(LifeCycle.REF); } controlElement.destroy(); setContext(originalContext); } catch (Exception exception) { if(exception instanceof MissingAttributeException == false) { exception.printStackTrace(); } CapoApplication.logger.log(Level.WARNING, "Couldn't process "+XPath.getXPath(node)+"\nREASON: "+(exception.getMessage() == null ? exception.getCause().toString() : exception.getMessage())); setContext(originalContext); throw exception; } } else { CapoApplication.logger.log(Level.WARNING, "Don't know what to do with: "+XPath.getXPath(controlElementDeclaration)); } } if (this instanceof GroupElement && parentGroup != null) { parentGroup.closeResourceDescriptors(LifeCycle.GROUP); } //cleanup this element destroy(); } @Override public void init(Element controlElementDeclaration, ControlElement parentControlElement, Group parentGroup, ControllerClientRequestProcessor controllerClientRequestProcessor) throws Exception { this.parentGroup = parentGroup; this.originalControlElementDeclaration = controlElementDeclaration; if (parentGroup != null) { this.controlElementDeclaration = (Element) parentGroup.replaceVarsInAttributeValues(controlElementDeclaration); } else { this.controlElementDeclaration = controlElementDeclaration; } this.controllerClientRequestProcessor = controllerClientRequestProcessor; this.parentControlElement = parentControlElement; } @Override public Group getParentGroup() { return parentGroup; } public void setParentGroup(Group parentGroup) { this.parentGroup = parentGroup; } @Override public ControlElement getParentControlElement() { return this.parentControlElement; } public void setParentControlElement(ControlElement parentControlElement) { this.parentControlElement = parentControlElement; } @Override public Element getOriginalControlElementDeclaration() { return this.originalControlElementDeclaration; } public void setOriginalControlElementDeclaration(Element originalControlElementDeclaration) { this.originalControlElementDeclaration = originalControlElementDeclaration; } @Override public Element getControlElementDeclaration() { return this.controlElementDeclaration; } public void setControlElementDeclaration(Element controlElementDeclaration) { this.controlElementDeclaration = controlElementDeclaration; } @Override public ControllerClientRequestProcessor getControllerClientRequestProcessor() { return this.controllerClientRequestProcessor; } @Override public void destroy() throws Exception { CapoApplication.logger.log(Level.FINE, "Destroying: "+getElementName()); } public String getAttributeValue(Enum attributeEnum) { if (getControlElementDeclaration() == null) { return ""; } else { return getControlElementDeclaration().getAttribute(attributeEnum.toString()); } } /** * * @param name * @return true if and only if value equals 'true', case is ignored. All other values return false; */ public boolean getAttributeBooleanValue(Enum attributeEnum) { if (getControlElementDeclaration() != null) { if (getControlElementDeclaration().hasAttribute(attributeEnum.toString())) { if (getAttributeValue(attributeEnum).equalsIgnoreCase("true")) { return true; } } } return false; } /** * * @param name * @return true if and only if value equals 'true', case is ignored. All other values return false; */ public boolean getBooleanValue(Enum name) throws Exception { if (getValue(name) != null) { if (getValue(name).equalsIgnoreCase("true")) { return true; } } return false; } public String getValue(Enum name) throws Exception { if (getParentGroup() != null) { return getParentGroup().getVarValue(name.toString()); } else { return ""; } } @Override public String getElementName() { return this.getClass().getAnnotation(ControlElementProvider.class).name(); } @Override public Enum[] getMissingAttributes() { Vector<Enum> missingAttributeVector = new Vector<Enum>(); Enum[] requiredAttributes = getRequiredAttributes(); for (Enum requiredAttribute : requiredAttributes) { if (getControlElementDeclaration().hasAttribute(requiredAttribute.toString()) == false) { missingAttributeVector.add(requiredAttribute); } } return missingAttributeVector.toArray(new Enum[]{}); } public void setContext(Object context) { if (Thread.currentThread() instanceof ContextThread) { ContextThread contextThread = (ContextThread) Thread.currentThread(); contextThread.setContext(context); } } public Object getContext() { if (Thread.currentThread() instanceof ContextThread) { ContextThread contextThread = (ContextThread) Thread.currentThread(); return contextThread.getContext(); } else { return null; } } public Node getContextNode() { Object context = getContext(); Node contextNode = null; if (context != null && context instanceof ControlElement) { contextNode = ((ControlElement)context).getControlElementDeclaration(); } else if (context != null && context instanceof Node) { contextNode = (Node) context; } return contextNode; } public ControlElement getContextControlElement() { Object context = getContext(); if (context != null && context instanceof ControlElement) { return ((ControlElement)context); } else { return null; } } public String getControlElementMD5() throws Exception { return XPath.getElementMD5(getControlElementDeclaration()); } }