org.directwebremoting.impl.DwrXmlConfigurator Maven / Gradle / Ivy
package org.directwebremoting.impl;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.ServletContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.directwebremoting.AjaxFilter;
import org.directwebremoting.Container;
import org.directwebremoting.extend.AccessControl;
import org.directwebremoting.extend.AjaxFilterManager;
import org.directwebremoting.extend.Configurator;
import org.directwebremoting.extend.ConverterManager;
import org.directwebremoting.extend.Creator;
import org.directwebremoting.extend.CreatorManager;
import org.directwebremoting.extend.MethodDeclaration;
import org.directwebremoting.extend.OverrideProperty;
import org.directwebremoting.extend.ParameterProperty;
import org.directwebremoting.extend.Property;
import org.directwebremoting.util.LocalUtil;
import org.directwebremoting.util.LogErrorHandler;
import org.directwebremoting.util.Loggers;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* A configurator that gets its configuration by reading a dwr.xml file.
* @author Joe Walker [joe at getahead dot ltd dot uk]
*/
public class DwrXmlConfigurator implements Configurator
{
/**
* Setter for the resource name that we can use to read a file from the
* servlet context
* @param servletContext ...
* @param servletResourceName The name to lookup
* @throws IOException On file read failure
* @throws ParserConfigurationException On XML setup failure
* @throws SAXException On XML parse failure
*/
public void setServletResourceName(ServletContext servletContext, String servletResourceName) throws IOException, ParserConfigurationException, SAXException
{
this.servletResourceName = servletResourceName;
InputStream in = null;
try
{
in = servletContext.getResourceAsStream(servletResourceName);
if (in == null)
{
throw new IOException("Missing config file: '" + servletResourceName + "'");
}
Loggers.STARTUP.debug("Configuring from servlet resource: " + servletResourceName);
setInputStream(in);
}
finally
{
LocalUtil.close(in);
}
}
/**
* Setter for a classpath based lookup
* @param classResourceName The resource to lookup in the classpath
* @throws IOException On file read failure
* @throws ParserConfigurationException On XML setup failure
* @throws SAXException On XML parse failure
*/
public void setClassResourceName(String classResourceName) throws IOException, ParserConfigurationException, SAXException
{
this.classResourceName = classResourceName;
InputStream in = LocalUtil.getInternalResourceAsStream(classResourceName);
if (in == null)
{
throw new IOException("Missing config file: '" + classResourceName + "'");
}
Loggers.STARTUP.debug("Configuring from class resource: " + classResourceName);
setInputStream(in);
}
/**
* Setter for a direct input stream to configure from
* @param in The input stream to read from.
* @throws IOException On file read failure
* @throws ParserConfigurationException On XML setup failure
* @throws SAXException On XML parse failure
*/
public void setInputStream(InputStream in) throws ParserConfigurationException, SAXException, IOException
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(true);
DocumentBuilder db = dbf.newDocumentBuilder();
db.setEntityResolver(new DTDEntityResolver());
db.setErrorHandler(new LogErrorHandler());
document = db.parse(in);
}
/**
* To set the configuration document directly
* @param document The new configuration document
*/
public void setDocument(Document document)
{
this.document = document;
}
/* (non-Javadoc)
* @see org.directwebremoting.Configurator#configure(org.directwebremoting.Container)
*/
public void configure(Container container)
{
accessControl = container.getBean(AccessControl.class);
ajaxFilterManager = container.getBean(AjaxFilterManager.class);
converterManager = container.getBean(ConverterManager.class);
creatorManager = container.getBean(CreatorManager.class);
Element root = document.getDocumentElement();
NodeList rootChildren = root.getChildNodes();
for (int i = 0; i < rootChildren.getLength(); i++)
{
Node node = rootChildren.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE)
{
Element child = (Element) node;
if (child.getNodeName().equals(ELEMENT_INIT))
{
loadInits(child);
}
else if (child.getNodeName().equals(ELEMENT_ALLOW))
{
loadAllows(child);
}
else if (child.getNodeName().equals(ELEMENT_SIGNATURES))
{
loadSignature(child);
}
}
}
}
/**
* Internal method to load the init element
* @param child The element to read
*/
private void loadInits(Element child)
{
NodeList inits = child.getChildNodes();
for (int j = 0; j < inits.getLength(); j++)
{
if (inits.item(j).getNodeType() == Node.ELEMENT_NODE)
{
Element initer = (Element) inits.item(j);
if (initer.getNodeName().equals(ATTRIBUTE_CREATOR))
{
String id = initer.getAttribute(ATTRIBUTE_ID);
String className = initer.getAttribute(ATTRIBUTE_CLASS);
creatorManager.addCreatorType(id, className);
}
else if (initer.getNodeName().equals(ATTRIBUTE_CONVERTER))
{
String id = initer.getAttribute(ATTRIBUTE_ID);
String className = initer.getAttribute(ATTRIBUTE_CLASS);
converterManager.addConverterType(id, className);
}
}
}
}
/**
* Internal method to load the create/convert elements
* @param child The element to read
*/
private void loadAllows(Element child)
{
NodeList allows = child.getChildNodes();
for (int j = 0; j < allows.getLength(); j++)
{
if (allows.item(j).getNodeType() == Node.ELEMENT_NODE)
{
Element allower = (Element) allows.item(j);
if (allower.getNodeName().equals(ELEMENT_CREATE))
{
loadCreate(allower);
}
else if (allower.getNodeName().equals(ELEMENT_CONVERT))
{
loadConvert(allower);
}
else if (allower.getNodeName().equals(ELEMENT_FILTER))
{
loadFilter(allower);
}
}
}
}
/**
* Internal method to load the convert element
* @param allower The element to read
*/
private void loadConvert(Element allower)
{
String match = allower.getAttribute(ATTRIBUTE_MATCH);
String type = allower.getAttribute(ATTRIBUTE_CONVERTER);
try
{
Map params = createSettingMap(allower);
converterManager.addConverter(match, type, params);
}
catch (NoClassDefFoundError ex)
{
Loggers.STARTUP.info("Convertor '" + type + "' not loaded due to NoClassDefFoundError. (match='" + match + "'). Cause: " + ex.getMessage());
}
catch (Exception ex)
{
Loggers.STARTUP.error("Failed to add convertor: match=" + match + ", type=" + type, ex);
}
}
/**
* Internal method to load the create element
* @param allower The element to read
*/
private void loadCreate(Element allower)
{
String type = allower.getAttribute(ATTRIBUTE_CREATOR);
String javascript = allower.getAttribute(ATTRIBUTE_JAVASCRIPT);
try
{
Map params = createSettingMap(allower);
creatorManager.addCreator(type, params);
processPermissions(javascript, allower);
processAuth(javascript, allower);
processParameters(javascript, allower);
processAjaxFilters(javascript, allower);
}
catch (NoClassDefFoundError ex)
{
Loggers.STARTUP.info("Creator '" + type + "' not loaded due to NoClassDefFoundError. (javascript='" + javascript + "'). Cause: " + ex.getMessage());
}
catch (Exception ex)
{
Loggers.STARTUP.error("Failed to add creator: type=" + type + ", javascript=" + javascript, ex);
}
}
/**
* Internal method to load the convert element
* @param allower The element to read
*/
private void loadFilter(Element allower)
{
String type = allower.getAttribute(ATTRIBUTE_CLASS);
try
{
Class> impl = LocalUtil.classForName(type);
AjaxFilter object = (AjaxFilter) impl.getDeclaredConstructor().newInstance();
LocalUtil.setParams(object, createSettingMap(allower), ignore);
ajaxFilterManager.addAjaxFilter(object);
}
catch (ClassCastException ex)
{
Loggers.STARTUP.error(type + " does not implement " + AjaxFilter.class.getName(), ex);
}
catch (NoClassDefFoundError ex)
{
Loggers.STARTUP.info("Missing class for filter (class='" + type + "'). Cause: " + ex.getMessage());
}
catch (Exception ex)
{
Loggers.STARTUP.error("Failed to add filter: class=" + type, ex);
}
}
/**
* Create a parameter map from nested
* elements
* @param parent The parent element
* @return A map of parameters
*/
private static Map createSettingMap(Element parent)
{
Map params = new HashMap();
// Go through the attributes in the allower element, adding to the param map
NamedNodeMap attrs = parent.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
{
Node node = attrs.item(i);
String name = node.getNodeName();
String value = node.getNodeValue();
params.put(name, value);
}
// Go through the param elements in the allower element, adding to the param map
NodeList locNodes = parent.getElementsByTagName(ELEMENT_PARAM);
for (int i = 0; i < locNodes.getLength(); i++)
{
// Since this comes from getElementsByTagName we can assume that
// all the nodes are elements.
Element element = (Element) locNodes.item(i);
// But getElementsByTagName(ELEMENT_PARAM) includes param nodes that
// are nested down inside filters, so we need to check that the
// parent node is 'parent'. $&*?! DOM!
if (element.getParentNode() != parent)
{
continue;
}
String name = element.getAttribute(ATTRIBUTE_NAME);
if (name != null)
{
String value = element.getAttribute(ATTRIBUTE_VALUE);
if (value == null || value.length() == 0)
{
StringBuffer buffer = new StringBuffer();
NodeList textNodes = element.getChildNodes();
for (int j = 0; j < textNodes.getLength(); j++)
{
buffer.append(textNodes.item(j).getNodeValue());
}
value = buffer.toString();
}
params.put(name, value);
}
}
return params;
}
/**
* Process the include and exclude elements, passing them on to the creator
* manager.
* @param javascript The name of the creator
* @param parent The container of the include and exclude elements.
*/
private void processPermissions(String javascript, Element parent)
{
NodeList incNodes = parent.getElementsByTagName(ELEMENT_INCLUDE);
for (int i = 0; i < incNodes.getLength(); i++)
{
Element include = (Element) incNodes.item(i);
String method = include.getAttribute(ATTRIBUTE_METHOD);
accessControl.addIncludeRule(javascript, method);
if (include.hasAttribute(ATTRIBUTE_ROLE))
{
String role = include.getAttribute(ATTRIBUTE_ROLE);
accessControl.addRoleRestriction(javascript, method, role);
}
}
NodeList excNodes = parent.getElementsByTagName(ELEMENT_EXCLUDE);
for (int i = 0; i < excNodes.getLength(); i++)
{
Element include = (Element) excNodes.item(i);
String method = include.getAttribute(ATTRIBUTE_METHOD);
accessControl.addExcludeRule(javascript, method);
}
}
/**
* J2EE role based method level security added here.
* @param javascript The name of the creator
* @param parent The container of the include and exclude elements.
*/
private void processAuth(String javascript, Element parent)
{
NodeList nodes = parent.getElementsByTagName(ELEMENT_AUTH);
for (int i = 0; i < nodes.getLength(); i++)
{
Element include = (Element) nodes.item(i);
String method = include.getAttribute(ATTRIBUTE_METHOD);
String role = include.getAttribute(ATTRIBUTE_ROLE);
accessControl.addRoleRestriction(javascript, method, role);
}
}
/**
* J2EE role based method level security added here.
* @param javascript The name of the creator
* @param parent The container of the include and exclude elements.
*/
private void processAjaxFilters(String javascript, Element parent)
{
NodeList nodes = parent.getElementsByTagName(ELEMENT_FILTER);
for (int i = 0; i < nodes.getLength(); i++)
{
Element include = (Element) nodes.item(i);
String type = include.getAttribute(ATTRIBUTE_CLASS);
AjaxFilter filter = LocalUtil.classNewInstance(javascript, type, AjaxFilter.class);
if (filter != null)
{
LocalUtil.setParams(filter, createSettingMap(include), ignore);
ajaxFilterManager.addAjaxFilter(filter, javascript);
}
}
}
/**
* Parse and extra type info from method signatures
* @param element The element to read
*/
private void loadSignature(Element element)
{
StringBuffer sigtext = new StringBuffer();
// This coagulates text nodes, not sure if we need to do this?
element.normalize();
NodeList nodes = element.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++)
{
Node node = nodes.item(i);
short type = node.getNodeType();
if (type != Node.TEXT_NODE && type != Node.CDATA_SECTION_NODE)
{
Loggers.STARTUP.warn("Ignoring illegal node type: " + type);
continue;
}
sigtext.append(node.getNodeValue());
}
SignatureParser sigp = new SignatureParser(converterManager, creatorManager);
sigp.parse(sigtext.toString());
}
/**
* Collections often have missing information. This helps fill the missing
* data in.
* @param javascript The name of the creator
* @param parent The container of the include and exclude elements.
* @throws ClassNotFoundException If the type attribute can't be converted into a Class
*/
private void processParameters(String javascript, Element parent) throws ClassNotFoundException
{
NodeList nodes = parent.getElementsByTagName(ELEMENT_PARAMETER);
for (int i = 0; i < nodes.getLength(); i++)
{
Element include = (Element) nodes.item(i);
String methodName = include.getAttribute(ATTRIBUTE_METHOD);
// Try to find the method that we are annotating
Creator creator = creatorManager.getCreator(javascript, true);
Class> dest = creator.getType();
Method method = null;
for (Method test : dest.getMethods())
{
if (test.getName().equals(methodName))
{
if (method == null)
{
method = test;
}
else
{
Loggers.STARTUP.warn("Setting extra type info to overloaded methods may fail with ");
}
}
}
if (method == null)
{
Loggers.STARTUP.error("Unable to find method called: " + methodName + " on type: " + dest.getName() + " from creator: " + javascript);
continue;
}
String number = include.getAttribute(ATTRIBUTE_NUMBER);
int paramNo = Integer.parseInt(number);
String types = include.getAttribute(ATTRIBUTE_TYPE);
StringTokenizer st = new StringTokenizer(types, ",");
int j = 0;
while (st.hasMoreTokens())
{
String type = st.nextToken();
Class> clazz = LocalUtil.classForName(type.trim());
ParameterProperty parentProperty = new ParameterProperty(new MethodDeclaration(method), paramNo);
Property child = parentProperty.createChild(j);
child = converterManager.checkOverride(child);
Property replacement = new OverrideProperty(clazz);
converterManager.setOverrideProperty(child, replacement);
j++;
}
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
if (servletResourceName != null)
{
return "DwrXmlConfigurator[ServletResource:" + servletResourceName + "]";
}
else
{
return "DwrXmlConfigurator[ClassResource:" + classResourceName + "]";
}
}
/**
* The parsed document
*/
private Document document;
/**
* The properties that we don't warn about if they don't exist.
*/
private static final List ignore = Arrays.asList("class");
/**
* What AjaxFilters apply to which Ajax calls?
*/
private AjaxFilterManager ajaxFilterManager = null;
/**
* The converter manager that decides how parameters are converted
*/
private ConverterManager converterManager = null;
/**
* The DefaultCreatorManager to which we delegate creation of new objects.
*/
private CreatorManager creatorManager = null;
/**
* The security manager
*/
private AccessControl accessControl = null;
/**
* For debug purposes, the classResourceName that we were configured with.
* Either this or {@link #servletResourceName} will be null
*/
private String classResourceName;
/**
* For debug purposes, the servletResourceName that we were configured with
* Either this or {@link #classResourceName} will be null
*/
private String servletResourceName;
/*
* The element names
*/
private static final String ELEMENT_INIT = "init";
private static final String ELEMENT_ALLOW = "allow";
private static final String ELEMENT_CREATE = "create";
private static final String ELEMENT_CONVERT = "convert";
private static final String ELEMENT_PARAM = "param";
private static final String ELEMENT_INCLUDE = "include";
private static final String ELEMENT_EXCLUDE = "exclude";
private static final String ELEMENT_PARAMETER = "parameter";
private static final String ELEMENT_AUTH = "auth";
private static final String ELEMENT_SIGNATURES = "signatures";
private static final String ELEMENT_FILTER = "filter";
/*
* The attribute names
*/
private static final String ATTRIBUTE_ID = "id";
private static final String ATTRIBUTE_CLASS = "class";
private static final String ATTRIBUTE_CONVERTER = "converter";
private static final String ATTRIBUTE_MATCH = "match";
private static final String ATTRIBUTE_JAVASCRIPT = "javascript";
private static final String ATTRIBUTE_CREATOR = "creator";
private static final String ATTRIBUTE_NAME = "name";
private static final String ATTRIBUTE_VALUE = "value";
private static final String ATTRIBUTE_METHOD = "method";
private static final String ATTRIBUTE_ROLE = "role";
private static final String ATTRIBUTE_NUMBER = "number";
private static final String ATTRIBUTE_TYPE = "type";
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy