http://git-wip-us.apache.org/repos/asf/flex-blazeds/blob/8315f8fa/common/src/main/java/flex/messaging/LocalizedException.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/flex/messaging/LocalizedException.java b/common/src/main/java/flex/messaging/LocalizedException.java new file mode 100644 index 0000000..641f7be --- /dev/null +++ b/common/src/main/java/flex/messaging/LocalizedException.java @@ -0,0 +1,378 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package flex.messaging; + +import java.util.Locale; + +import flex.messaging.util.PropertyStringResourceLoader; +import flex.messaging.util.ResourceLoader; + +/** + * The LocalizedException class is the base class for server + * exceptions that use localized error message and details strings. This class overloads + * <code>setMessage</code> and <code>setDetails</code> to support passing an error + * number and an optional message variant that is used to look up a localized error + * message or details string using a <code>ResourceLoader</code>. These methods also + * set the number property of the exception instance. + * + * The various overloads optionally support specifying a target locale as well as + * arguments to substitute into the localized string if it is parameterized. + * + * Localized error message and details strings are stored in the flex.messaging.errors + * resource bundle. Entries must have the following format. + * <ul> + * <li>Error message: {number}[-{variant}]={message}</li> + * <li>Error details: {number}[-{variant}]-details={details}</li> + * </ul> + * + * @see ResourceLoader + */ +public class LocalizedException extends RuntimeException +{ + /** - transient, the resourceLoader for localized strings doesn't need to serialize. */ + protected transient ResourceLoader resourceLoader; + + protected int number; + + protected String message; + + protected String details; + + protected Throwable rootCause; + + /** + * This number was generated using the 'serialver' command line tool. + * This number should remain consistent with the version used by + * ColdFusion to communicate with the message broker over RMI. + */ + private static final long serialVersionUID = 7980539484335065853L; + + /** + * Create a LocalizedException with the default ResourceLoader. + */ + public LocalizedException() + { + super(); + } + + /** + * Create a LocalizedException that will use the specified + * ResourceLoader. + * + * @param resourceLoader The resource loader to use. + */ + public LocalizedException(ResourceLoader resourceLoader) + { + this.resourceLoader = resourceLoader; + } + + /** + * Returns the exception details. + * + * @return The exception details. + */ + public String getDetails() + { + return details; + } + + /** + * Sets the exception details. + * + * @param details The exception details. + */ + public void setDetails(String details) + { + this.details = details; + } + + /** + * Sets the message property to a localized string based on error number. + * + * @param number The error number for this exception instance. + */ + public void setMessage(int number) + { + setMessage(number, null, null, null); + } + + /** + * Sets the message property to a localized string based on error number and target locale. + * + * @param number The error number for this exception instance. + * @param locale The target locale for error message lookup. + */ + public void setMessage(int number, Locale locale) + { + setMessage(number, null, locale, null); + } + + /** + * Sets the message property to a localized string based on error number. + * The passed arguments are substituted into the parameterized error message string. + * + * @param number The error number for this exception instance. + * @param arguments The arguments to substitute into the error message. + */ + public void setMessage(int number, Object[] arguments) + { + setMessage(number, null, null, arguments); + } + + /** + * Sets the message property to a localized string based on error number and variant. + * + * @param number The error number for this exception instance. + * @param variant The variant of the error message for this instance. + */ + public void setMessage(int number, String variant) + { + setMessage(number, variant, null, null); + } + + /** + * Sets the message property to a localized string based on error number, variant + * and target locale. + * + * @param number The error number for this exception instance. + * @param variant The variant of the error message for this instance. + * @param locale The target locale for error message lookup. + */ + public void setMessage(int number, String variant, Locale locale) + { + setMessage(number, variant, locale, null); + } + + /** + * Sets the message property to a localized string based on error number and variant. + * The passed arguments are substituted into the parameterized error message string. + * + * @param number The error number for this exception instance. + * @param variant The varient of the error message for this instance. + * @param arguments The arguments to substitute into the error message. + */ + public void setMessage(int number, String variant, Object[] arguments) + { + setMessage(number, variant, null, arguments); + } + + /** + * Sets the message property to a localized string based on error number, variant and + * target locale. The passed arguments are substituted into the parameterized error + * message string. + * + * @param number The error number for this exception instance. + * @param variant The variant of the error message for this instance. + * @param locale The target locale for error message lookup. + * @param arguments The arguments to substitute into the error message. + */ + public void setMessage(int number, String variant, Locale locale, Object[] arguments) + { + setNumber(number); + ResourceLoader resources = getResourceLoader(); + setMessage(resources.getString(generateFullKey(number, variant), locale, arguments)); + } + + /** + * Returns the exception message. + * + * @return The exception message. + */ + public String getMessage() + { + return message; + } + + /** + * Sets the exception message. + * + * @param message The exception message. + */ + public void setMessage(String message) + { + this.message = message; + } + + /** + * Sets the localized exception number. + * + * @param number The localized exception number. + */ + public void setNumber(int number) + { + this.number = number; + } + + /** + * Returns the localized exception number. + * + * @return The localized exception number. + */ + public int getNumber() + { + return number; + } + + /** + * Sets the details property to a localized string based on error number. + * + * @param number The error number to lookup details for. + */ + public void setDetails(int number) + { + setDetails(number, null, null, null); + } + + /** + * Sets the details property to a localized string based on error number and variant. + * + * @param number The error number to lookup details for. + * @param variant The variant of the details string to lookup. + */ + public void setDetails(int number, String variant) + { + setDetails(number, variant, null, null); + } + + /** + * Sets the details property to a localized string based on error number, variant + * and target locale. + * + * @param number The error number to lookup details for. + * @param variant The variant of the details string to lookup. + * @param locale The target locale for the lookup. + */ + public void setDetails(int number, String variant, Locale locale) + { + setDetails(number, variant, locale, null); + } + + /** + * Sets the details property to a localized string based on error number and variant. + * The passed arguments are substituted into the parameterized error details string. + * + * @param number The error number to lookup details for. + * @param variant The variant of the details string to lookup. + * @param arguments The arguments to substitute into the details string. + */ + public void setDetails(int number, String variant, Object[] arguments) + { + setDetails(number, variant, null, arguments); + } + + + /** + * Sets the details property to a localized string based on error number, variant, + * and target locale. The passed arguments are substituted into the parameterized error + * details string. + * + * @param number The error number to lookup a localized details string for. + * @param variant The variant of the details string to lookup. + * @param locale The target locale for the lookup. + * @param arguments The arguments to substitute into the details string. + */ + public void setDetails(int number, String variant, Locale locale, Object[] arguments) + { + setNumber(number); + ResourceLoader resources = getResourceLoader(); + setDetails(resources.getString(generateDetailsKey(number, variant), locale, arguments)); + } + + /** + * Returns the root cause for this exception. + * + * @return The root cause for this exception. + */ + public Throwable getRootCause() + { + return rootCause; + } + + /** + * Sets the root cause for this exception. + * + * @param cause The root cause for this exception. + */ + public void setRootCause(Throwable cause) + { + rootCause = cause; + // Assign through to the base cause property to include it in general stack traces. + initCause(cause); + } + + /** + * Returns the <code>ResourceLoader</code> used to load localized strings. + * + * @return The <code>ResourceLoader</code> used to load localized strings. + */ + protected ResourceLoader getResourceLoader() + { + if (resourceLoader == null) + resourceLoader = new PropertyStringResourceLoader(); + + return resourceLoader; + } + + /** + * Generates the full key to lookup a localized error message based on an error number + * and an optional variant followed by a "-details" suffix. If the variant is null, the + * lookup key is the error number. + * + * @param number The error number. + * @param variant The variant of the error message. + * @return The full lookup key for a localized error message. + */ + private String generateFullKey(int number, String variant) + { + return (variant != null) ? (number + "-" + variant) : String.valueOf(number); + } + + /** + * Generates the full key to lookup a localized details string based on an error number + * and an optional variant followed by a "-details" suffix. If the variant is null, the + * lookup key is the error number followed by a "-details" suffix. + * + * @param number The error number. + * @param variant The variant of the details message. + * @return The full lookup key for a localized details message. + */ + private String generateDetailsKey(int number, String variant) + { + return (generateFullKey(number, variant) + "-details"); + } + + /** + * Returns a string represenation of the exception. + * + * @return A string representation of the exception. + */ + public String toString() + { + String result = super.toString(); + if (details != null) + { + StringBuffer buffer = new StringBuffer(result); + if (!result.endsWith(".")) + { + buffer.append("."); + } + buffer.append(' ').append(details); + result = buffer.toString(); + } + return result; + } +}
http://git-wip-us.apache.org/repos/asf/flex-blazeds/blob/8315f8fa/common/src/main/java/flex/messaging/config/AbstractConfigurationParser.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/flex/messaging/config/AbstractConfigurationParser.java b/common/src/main/java/flex/messaging/config/AbstractConfigurationParser.java new file mode 100644 index 0000000..297f6fc --- /dev/null +++ b/common/src/main/java/flex/messaging/config/AbstractConfigurationParser.java @@ -0,0 +1,556 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package flex.messaging.config; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import flex.messaging.util.FileUtils; + +/** + * Provides a common base for DOM / XPath based ConfigurationParsers. + */ +public abstract class AbstractConfigurationParser implements ConfigurationParser, ConfigurationConstants, EntityResolver +{ + protected ServicesConfiguration config; + protected DocumentBuilder docBuilder; + protected ConfigurationFileResolver fileResolver; + protected TokenReplacer tokenReplacer; + + private Map fileByDocument = new HashMap(); + + protected AbstractConfigurationParser() + { + initializeDocumentBuilder(); + tokenReplacer = new TokenReplacer(); + } + + /** + * Parse the configurations in the configuration file. + * + * @param path the configuration file path + * @param fileResolver the ConfigurationFileResolver object + * @param config the ServicesConfiguration object + **/ + public void parse(String path, ConfigurationFileResolver fileResolver, ServicesConfiguration config) + { + this.config = config; + this.fileResolver = fileResolver; + Document doc = loadDocument(path, fileResolver.getConfigurationFile(path)); + initializeExpressionQuery(); + parseTopLevelConfig(doc); + } + + /** + * Report Tokens. + **/ + public void reportTokens() + { + tokenReplacer.reportTokens(); + } + + protected void initializeDocumentBuilder() + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setIgnoringComments(true); + factory.setIgnoringElementContentWhitespace(true); + + try + { + docBuilder = factory.newDocumentBuilder(); + docBuilder.setEntityResolver(this); + } + catch (ParserConfigurationException ex) + { + // Error initializing configuration parser. + ConfigurationException e = new ConfigurationException(); + e.setMessage(PARSER_INIT_ERROR); + e.setRootCause(ex); + throw e; + } + } + + protected Document loadDocument(String path, InputStream in) + { + try + { + Document doc; + + if (!in.markSupported()) + in = new BufferedInputStream(in); + + // Consume UTF-8 Byte Order Mark (BOM). The InputStream must support mark() + // as FileUtils.consumeBOM sets a mark for 3 bytes in order to check for a BOM. + String encoding = FileUtils.consumeBOM(in, null); + if (FileUtils.UTF_8.equals(encoding) || FileUtils.UTF_16.equals(encoding)) + { + InputSource inputSource = new InputSource(in); + inputSource.setEncoding(encoding); + doc = docBuilder.parse(inputSource); + } + else + { + doc = docBuilder.parse(in); + } + + addFileByDocument(path, doc); + doc.getDocumentElement().normalize(); + return doc; + } + catch (SAXParseException ex) + { + Integer line = new Integer(ex.getLineNumber()); + Integer col = new Integer(ex.getColumnNumber()); + String message = ex.getMessage(); + + // Configuration error on line {line}, column {col}: '{message}' + ConfigurationException e = new ConfigurationException(); + e.setMessage(XML_PARSER_ERROR, new Object[]{line, col, message}); + e.setRootCause(ex); + throw e; + } + catch (SAXException ex) + { + ConfigurationException e = new ConfigurationException(); + e.setMessage(ex.getMessage()); + e.setRootCause(ex); + throw e; + } + catch (IOException ex) + { + ConfigurationException e = new ConfigurationException(); + e.setMessage(ex.getMessage()); + e.setRootCause(ex); + throw e; + } + } + + protected void addFileByDocument(String path, Node node) + { + String shortPath = path; + if (shortPath != null && ((shortPath.indexOf('/') != -1) || (shortPath.indexOf("\\") != -1)) ) + { + int start = 0; + start = shortPath.lastIndexOf('/'); + if (start == -1) + { + start = shortPath.lastIndexOf("\\"); + } + shortPath = path.substring(start + 1); + } + fileByDocument.put(node, shortPath); + } + + protected String getSourceFileOf(Node node) + { + return (String)fileByDocument.get(node.getOwnerDocument()); + } + + protected abstract void parseTopLevelConfig(Document doc); + + protected abstract void initializeExpressionQuery(); + protected abstract Node selectSingleNode(Node source, String expression); + protected abstract NodeList selectNodeList(Node source, String expression); + protected abstract Object evaluateExpression(Node source, String expression); + + /** + * Recursively processes all child elements for each of the properties in the provided + * node list. + * <p> + * If a property is a simple element with a text value then it is stored as a String + * using the element name as the property name. If the same element appears again then + * the element is converted to a List of values and further occurences are simply added + * to the List. + * </p> + * <p> + * If a property element has child elements the children are recursively processed + * and added as a Map. + * </p> + * <p> + * The sourceFileName argument is used as a parameter for token replacement in order + * to generate a meaningful error message when a token is failed to be replaced. + * </p> + * @param properties the NodeList object + * @return ConfigMap the ConfigMap object + */ + public ConfigMap properties(NodeList properties) + { + return properties(properties, ConfigurationConstants.UNKNOWN_SOURCE_FILE); + } + + /** + * Recursively processes all child elements for each of the properties in the provided + * node list. + * + * @param properties the NodeList object + * @param sourceFileName the source file name + * @return ConfigMap the ConfigMap object + */ + public ConfigMap properties(NodeList properties, String sourceFileName) + { + int length = properties.getLength(); + ConfigMap map = new ConfigMap(length); + + for (int p = 0; p < length; p++) + { + Node prop = properties.item(p); + String propName = prop.getNodeName(); + + if (propName != null) + { + propName = propName.trim(); + if (prop.getNodeType() == Node.ELEMENT_NODE) + { + NodeList attributes = selectNodeList(prop, "@*"); + NodeList children = selectNodeList(prop, "*"); + if (children.getLength() > 0 || attributes.getLength() > 0) + { + ConfigMap childMap = new ConfigMap(); + + if (children.getLength() > 0) + childMap.addProperties(properties(children, sourceFileName)); + + if (attributes.getLength() > 0) + childMap.addProperties(properties(attributes, sourceFileName)); + + map.addProperty(propName, childMap); + } + else + { + // replace tokens before setting property + tokenReplacer.replaceToken(prop, sourceFileName); + String propValue = evaluateExpression(prop, ".").toString(); + map.addProperty(propName, propValue); + } + } + else + { + // replace tokens before setting property + tokenReplacer.replaceToken(prop, sourceFileName); + map.addProperty(propName, prop.getNodeValue()); + } + } + } + + return map; + } + + /** + * Get the item value by name if the item is present in the current node as attribute or child element. + * + * @param node the current Node object + * @param name item name + * @return String the value of item + **/ + public String getAttributeOrChildElement(Node node, String name) + { + String attr = evaluateExpression(node, "@" + name).toString().trim(); + if (attr.length() == 0) + { + attr = evaluateExpression(node, name).toString().trim(); + } + return attr; + } + + /** + * Check whether the required items are present in the current node as child elements. + * + * @param node the current Node object + * @param allowed the String array of the allowed items + **/ + public void allowedChildElements(Node node, String[] allowed) + { + NodeList children = selectNodeList(node, "*"); + + String unexpected = unexpected(children, allowed); + if (unexpected != null) + { + // Unexpected child element '{0}' found in '{1}'. + ConfigurationException ex = new ConfigurationException(); + Object[] args = {unexpected, node.getNodeName(), getSourceFilename(node)}; + ex.setMessage(UNEXPECTED_ELEMENT, args); + throw ex; + } + + NodeList textNodes = selectNodeList(node, "text()"); + for (int i = 0; i < textNodes.getLength(); i++) + { + String text = evaluateExpression(textNodes.item(i), ".").toString().trim(); + if (text.length() > 0) + { + //Unexpected text '{0}' found in '{1}'. + ConfigurationException ex = new ConfigurationException(); + Object[] args = {text, node.getNodeName(), getSourceFilename(node)}; + ex.setMessage(UNEXPECTED_TEXT, args); + throw ex; + } + } + } + + /** + * Check whether the required items are present in the current node as attributes. + * + * @param node the current Node object + * @param allowed the String array of the allowed items + **/ + + public void allowedAttributes(Node node, String[] allowed) + { + NodeList attributes = selectNodeList(node, "@*"); + String unexpectedAttribute = unexpected(attributes, allowed); + if (unexpectedAttribute != null) + { + //Unexpected attribute '{0}' found in '{1}'. + ConfigurationException ex = new ConfigurationException(); + Object[] args = + {unexpectedAttribute, node.getNodeName(), getSourceFilename(node)}; + ex.setMessage(UNEXPECTED_ATTRIBUTE, args); + throw ex; + } + } + + private String getSourceFilename(Node node) + { + return getSourceFileOf(node); + } + + /** + * Check whether the allowed items are present in the current node as elements or attributes. + * + * @param node the current Node object + * @param allowed the String array of the allowed items + **/ + + public void allowedAttributesOrElements(Node node, String[] allowed) + { + allowedAttributes(node, allowed); + allowedChildElements(node, allowed); + } + + /** + * Check whether the required items are present in the current node as elements or attributes. + * + * @param node the current Node object + * @param required the String array of the required items + **/ + public void requiredAttributesOrElements(Node node, String[] required) + { + String nodeName = node.getNodeName(); + NodeList attributes = selectNodeList(node, "@*"); + + List list = new ArrayList(); + for (int i = 0; i < required.length; i++) + { + list.add(required[i]); + } + + String missingAttribute = null; + + do + { + missingAttribute = required(attributes, list); + if (missingAttribute != null) + { + Node child = selectSingleNode(node, missingAttribute); + if (child != null) + { + list.remove(missingAttribute); + } + else + { + // Attribute '{0}' must be specified for element '{1}' + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(MISSING_ATTRIBUTE, new Object[]{missingAttribute, nodeName}); + throw ex; + } + } + } + while (missingAttribute != null && list.size() > 0); + } + + /** + * Check whether the required items are present in the current node (Child elements). + * + * @param node the current Node object + * @param required the String array of the required items + **/ + public void requiredChildElements(Node node, String[] required) + { + String nodeName = node.getNodeName(); + NodeList children = selectNodeList(node, "*"); + + List list = new ArrayList(); + for (int i = 0; i < required.length; i++) + { + list.add(required[i]); + } + + String missing = required(children, list); + if (missing != null) + { + // Child element '{0}' must be specified for element '{1}' + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(MISSING_ELEMENT, new Object[]{missing, nodeName}); + throw ex; + } + } + + /** + * Check whether there is any unexpected item in the node list object. + * + * @param attributes the current NodeList object + * @param allowed, the String array of allowed items + * @return Name of the first unexpected item from the list of allowed attributes, or null if all + * items present were allowed. + **/ + public String unexpected(NodeList attributes, String[] allowed) + { + for (int i = 0; i < attributes.getLength(); i++) + { + Node attrib = attributes.item(i); + String attribName = attrib.getNodeName(); + boolean match = false; + + for (int j = 0; j < allowed.length; j++) + { + String a = allowed[j]; + if (a.equals(attribName)) + { + match = true; + break; + } + } + + if (!match) + { + return attribName; + } + + // Go ahead and replace tokens in node values. + tokenReplacer.replaceToken(attrib, getSourceFilename(attrib)); + } + + return null; + } + + /** + * Check whether all the required items are present in the node list. + * @param attributes the NodeList object + * @param required a list of required items + * @return Name of the first missing item from the list of required attributes, or null if all required + * items were present. + **/ + public String required(NodeList attributes, List required) + { + for (int i = 0; i < required.size(); i++) + { + boolean found = false; + String req = (String)required.get(i); + + Node attrib = null; + for (int j = 0; j < attributes.getLength(); j++) + { + attrib = attributes.item(j); + String attribName = attrib.getNodeName(); + + if (req.equals(attribName)) + { + found = true; + break; + } + } + + if (!found) + { + return req; + } + + // Go ahead and replace tokens in node values. + tokenReplacer.replaceToken(attrib, getSourceFilename(attrib)); + + } + + return null; + } + + + /** + * Tests whether a configuration element's id is a valid + * identifier. An id must be a String that is not null, + * greater than 0 characters in length and not contain + * any of the list delimiter characters, i.e. commas, + * semi-colons or colons. + * @param id the Id String + * @return boolean true if the id string is valid identification + * @see ConfigurationConstants#LIST_DELIMITERS + */ + public static boolean isValidID(String id) + { + if (id != null && id.length() > 0 && id.length() < 256) + { + char[] chars = id.toCharArray(); + for (int i = 0; i < chars.length; i++) + { + char c = chars[i]; + if (LIST_DELIMITERS.indexOf(c) != -1) + { + return false; + } + } + + return true; + } + + return false; + } + + /** + * Implement {@link org.xml.sax.EntityResolver#resolveEntity(String, String)}. + * + * Flex Configuration does not need or use external entities, so disallow external entities + * to prevent external entity injection attacks. + * @param publicId the public Id + * @param systemId the system Id + * @return InputSource the InputSource object + * @throws SAXException when the parsing process failed with exceptions + * @throws IOException when the parsing process failed with exceptions + */ + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException + { + // Flex Configuration does not allow external entity defined by publicId {0} and SystemId {1} + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(EXTERNAL_ENTITY_NOT_ALLOW, new Object[] { publicId, systemId }); + throw ex; + } +} http://git-wip-us.apache.org/repos/asf/flex-blazeds/blob/8315f8fa/common/src/main/java/flex/messaging/config/AdapterSettings.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/flex/messaging/config/AdapterSettings.java b/common/src/main/java/flex/messaging/config/AdapterSettings.java new file mode 100644 index 0000000..8dea7ca --- /dev/null +++ b/common/src/main/java/flex/messaging/config/AdapterSettings.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package flex.messaging.config; + +/** + * A service must register the adapters that it will use + * to process messages. Each destination selects an adapter + * that processes the request by referring to it by id. + * <p> + * Adapters can also be configured with initialization + * properties. + * </p> + */ +public class AdapterSettings extends PropertiesSettings +{ + private final String id; + private String sourceFile; + private String className; + private boolean defaultAdapter; + + /** + * Used to construct a new set of properties to describe an adapter. Note + * that an identity is required in order for destinations to refer to this + * adapter. + * + * @param id the <code>String</code> representing the unique identity for + * this adapter. + */ + public AdapterSettings(String id) + { + super(); + this.id = id; + } + + /** + * The identity that destinations will refer to when assigning and adapter. + * + * @return the adapter identity as a <code>String</code>. + */ + public String getId() + { + return id; + } + + /** + * Gets the name of the Java class implementation for this adapter. + * + * @return String The name of the adapter implementation. + */ + public String getClassName() + { + return className; + } + + /** + * Sets name of the Java class implementation for this adapter. The + * implementation is resolved from the current classpath and must extend + * <code>flex.messaging.services.ServiceAdapter</code>. + * + * @param name the <code>String</code> + */ + public void setClassName(String name) + { + className = name; + } + + /** + * Returns a boolean flag that determines whether this adapter is the + * default for a service's destinations. Only one default adapter can be + * set for a given service. + * + * @return boolean true if this adapter is the default. + */ + public boolean isDefault() + { + return defaultAdapter; + } + + /** + * Sets a flag to determine whether an adapter will be used as the default + * (for example, in the event that a destination did not specify an adapter + * explicitly). + * + * Only one default can be set for a given service. + * + * @param b a <code>boolean</code> flag, true if this adapter should be + * used as the default for the service. + */ + public void setDefault(boolean b) + { + defaultAdapter = b; + } + + /** + * Internal use only. + * + */ + String getSourceFile() + { + return sourceFile; + } + + /** + * Internal use only. + * + */ + void setSourceFile(String file) + { + this.sourceFile = file; + } +} http://git-wip-us.apache.org/repos/asf/flex-blazeds/blob/8315f8fa/common/src/main/java/flex/messaging/config/ApacheXPathClientConfigurationParser.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/flex/messaging/config/ApacheXPathClientConfigurationParser.java b/common/src/main/java/flex/messaging/config/ApacheXPathClientConfigurationParser.java new file mode 100644 index 0000000..2ad8764 --- /dev/null +++ b/common/src/main/java/flex/messaging/config/ApacheXPathClientConfigurationParser.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package flex.messaging.config; + +import org.apache.xpath.CachedXPathAPI; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import javax.xml.transform.TransformerException; + +/** + * Uses Apache XPath on a DOM representation of a messaging configuration + * file. + * <p> + * NOTE: Since reference ids are used between elements, certain + * sections of the document need to be parsed first. + * </p> + * + * + */ +public class ApacheXPathClientConfigurationParser extends ClientConfigurationParser +{ + private CachedXPathAPI xpath; + + protected void initializeExpressionQuery() + { + this.xpath = new CachedXPathAPI(); + } + + protected Node selectSingleNode(Node source, String expression) + { + try + { + return xpath.selectSingleNode(source, expression); + } + catch (TransformerException transformerException) + { + throw wrapException(transformerException); + } + } + + protected NodeList selectNodeList(Node source, String expression) + { + try + { + return xpath.selectNodeList(source, expression); + } + catch (TransformerException transformerException) + { + throw wrapException(transformerException); + } + } + + protected Object evaluateExpression(Node source, String expression) + { + try + { + return xpath.eval(source, expression); + } + catch (TransformerException transformerException) + { + throw wrapException(transformerException); + } + } + + private ConfigurationException wrapException(TransformerException exception) + { + ConfigurationException result = new ConfigurationException(); + result.setDetails(PARSER_INTERNAL_ERROR); + result.setRootCause(exception); + return result; + } +} http://git-wip-us.apache.org/repos/asf/flex-blazeds/blob/8315f8fa/common/src/main/java/flex/messaging/config/ChannelSettings.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/flex/messaging/config/ChannelSettings.java b/common/src/main/java/flex/messaging/config/ChannelSettings.java new file mode 100644 index 0000000..badc6e2 --- /dev/null +++ b/common/src/main/java/flex/messaging/config/ChannelSettings.java @@ -0,0 +1,334 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package flex.messaging.config; +import flex.messaging.util.StringUtils; + +/** + * The channel configuration is intentionally generic so that + * other channels can be added in the future. This format also allows + * server-endpoint specific and client code-generation specific settings + * to be modified without affecting needing to update the configuration + * parser. + * + * + */ +public class ChannelSettings extends PropertiesSettings +{ + protected String id; + protected boolean remote; + protected String serverId; + private String sourceFile; + + protected SecurityConstraint constraint; + + // ENDPOINT + protected String uri; + protected int port; + protected String endpointType; + protected String clientType; + protected boolean serverOnly; + + protected String parsedUri; + protected boolean contextParsed; + protected String parsedClientUri; + protected boolean clientContextParsed; + + public ChannelSettings(String id) + { + this.id = id; + } + + public String getId() + { + return id; + } + + public boolean isRemote() + { + return remote; + } + + public void setRemote(boolean value) + { + remote = value; + } + + public String getServerId() + { + return serverId; + } + + public void setServerId(String value) + { + serverId = value; + } + + public String getClientType() + { + return clientType; + } + + public void setClientType(String type) + { + this.clientType = type; + } + + public boolean getServerOnly() + { + return serverOnly; + } + + public void setServerOnly(boolean serverOnly) + { + this.serverOnly = serverOnly; + } + + String getSourceFile() + { + return sourceFile; + } + + void setSourceFile(String sourceFile) + { + this.sourceFile = sourceFile; + } + + /** + * A return value of 0 denotes no port in channel url. + * + * @return the port number for this channel + * or 0 if channel url does not contain a port number + */ + public int getPort() + { + return port; + } + + public String getUri() + { + return uri; + } + + public void setUri(String uri) + { + this.uri = uri; + port = parsePort(uri); + port = (port == -1)? 0 : port; // Replace -1 with 0. + contextParsed = false; + clientContextParsed = false; + } + + public String getClientParsedUri(String contextPath) + { + if (!clientContextParsed) + parseClientUri(this, contextPath); + + return parsedClientUri; + } + + public String getEndpointType() + { + return endpointType; + } + + public void setEndpointType(String type) + { + this.endpointType = type; + } + + public SecurityConstraint getConstraint() + { + return constraint; + } + + public void setConstraint(SecurityConstraint constraint) + { + this.constraint = constraint; + } + + /** + * In this client version of the URI parser we're just looking to + * replace the context root tokens. + */ + private static void parseClientUri(ChannelSettings cs, String contextPath) + { + if (!cs.clientContextParsed) + { + String channelEndpoint = cs.getUri().trim(); + + // either {context-root} or {context.root} is legal + channelEndpoint = StringUtils.substitute(channelEndpoint, "{context-root}", ConfigurationConstants.CONTEXT_PATH_TOKEN); + + if ((contextPath == null) && (channelEndpoint.indexOf(ConfigurationConstants.CONTEXT_PATH_TOKEN) != -1)) + { + // context root must be specified before it is used + ConfigurationException e = new ConfigurationException(); + e.setMessage(ConfigurationConstants.UNDEFINED_CONTEXT_ROOT, new Object[]{cs.getId()}); + throw e; + } + + // simplify the number of combinations to test by ensuring our + // context path always starts with a slash + if (contextPath != null && !contextPath.startsWith("/")) + { + contextPath = "/" + contextPath; + } + + // avoid double-slashes from context root by replacing /{context.root} + // in a single replacement step + if (channelEndpoint.indexOf(ConfigurationConstants.SLASH_CONTEXT_PATH_TOKEN) != -1) + { + // but avoid double-slash for /{context.root}/etc when we have + // the default context root + if ("/".equals(contextPath) && !ConfigurationConstants.SLASH_CONTEXT_PATH_TOKEN.equals(channelEndpoint)) + contextPath = ""; + + channelEndpoint = StringUtils.substitute(channelEndpoint, ConfigurationConstants.SLASH_CONTEXT_PATH_TOKEN, contextPath); + } + // otherwise we have something like {server.name}:{server.port}{context.root}... + else + { + // but avoid double-slash for {context.root}/etc when we have + // the default context root + if ("/".equals(contextPath) && !ConfigurationConstants.CONTEXT_PATH_TOKEN.equals(channelEndpoint)) + contextPath = ""; + + channelEndpoint = StringUtils.substitute(channelEndpoint, ConfigurationConstants.CONTEXT_PATH_TOKEN, contextPath); + } + + cs.parsedClientUri = channelEndpoint; + cs.clientContextParsed = true; + } + } + + /** + * Returns the host name in the URL, or an empty <tt>String</tt> if the URL does not contain a host name. + * + * @param url The URL to parse for a host name. + * @return The host name or an empty <tt>String</tt> if the URL does not contain a host name. + */ + // The reason for this method, rather than just using java.net.URL, is that the Java class + // doesn't recognize many of our protocol types/schemes (e.g. rtmp, amfsocket, etc.) + public static String parseHost(String url) + { + int start = url.indexOf(":/"); + if (start == -1) + return ""; + + start = start + 3; // Advance past all of '://' to beginning of host name. + int end = url.indexOf('/', start); + String hostnameWithPort = end == -1 ? url.substring(start) : url.substring(start, end); + + // IPv6 hostnames may contain ':' but the full value is wrapped in [] - skip past if necessary. + int delim = hostnameWithPort.indexOf(']'); + delim = (delim != -1) ? hostnameWithPort.indexOf(':', delim) : hostnameWithPort.indexOf(':'); + if (delim == -1) // No port. + return hostnameWithPort; + else + return hostnameWithPort.substring(0, delim); + } + + /** + * Returns the port number specified in the URL, or 0 if the URL + * does not contain a port number, or -1 if the URL contains a server.port + * token. + * + * @param url The URL to parse for a contained port number. + * @return the port number in the URL, or 0 if the URL does not + * contain a port number, or -1 if the URL contains a server.port token. + */ + // The reason for this method, rather than just using java.net.URL, is that the Java class + // doesn't recognize many of our protocol types/schemes (e.g. rtmp, amfsocket, etc.) + public static int parsePort(String url) + { + // rtmp://localhost:2035/foo/bar + // Find first slash with colon + int start = url.indexOf(":/"); + if (start == -1) + return 0; + + // Second slash should be +1, so start 3 after for :// + start = start + 3; + int end = url.indexOf('/', start); + + // take everything up until the next slash for servername:port + String snp = end == -1 ? url.substring(start) : url.substring(start, end); + + // If IPv6 is in use, start looking after the square bracket. + int delim = snp.indexOf(']'); + delim = (delim > -1)? snp.indexOf(':', delim) : snp.indexOf(':'); + if (delim == -1) + return 0; + + int port = 0; + try + { + int p = Integer.parseInt(snp.substring(delim + 1)); + port = (p > 0)? p : 0; + } + catch (Throwable t) + { + port = -1; // To denote that the url contained server.port token. + } + return port; + } + + /** + * Remove protocol, host, port and context-root from a given url. + * Unlike parseClientUri, this method does not check if the channel + * setting has been parsed before. + * + * @param url Original url. + * @return Url with protocol, host, port and context-root removed. + */ + public static String removeTokens(String url) + { + String channelEndpoint = url.toLowerCase().trim(); + + // remove protocol and host info + if (channelEndpoint.startsWith("http://") || + channelEndpoint.startsWith("https://") || + channelEndpoint.startsWith("rtmp://") || + channelEndpoint.startsWith("rtmps://")) { + int nextSlash = channelEndpoint.indexOf('/', 8); + // Check to see if there is a 'next slash', and also that the next + // slash isn't the last character + if ((nextSlash > 0) && (nextSlash != channelEndpoint.length()-1)) + channelEndpoint = channelEndpoint.substring(nextSlash); + } + + // either {context-root} or {context.root} is legal + channelEndpoint = StringUtils.substitute(channelEndpoint, "{context-root}", ConfigurationConstants.CONTEXT_PATH_TOKEN); + + // Remove context path info + if (channelEndpoint.startsWith(ConfigurationConstants.CONTEXT_PATH_TOKEN)) + { + channelEndpoint = channelEndpoint.substring(ConfigurationConstants.CONTEXT_PATH_TOKEN.length()); + } + else if (channelEndpoint.startsWith(ConfigurationConstants.SLASH_CONTEXT_PATH_TOKEN)) + { + channelEndpoint = channelEndpoint.substring(ConfigurationConstants.SLASH_CONTEXT_PATH_TOKEN.length()); + } + + // We also don't match on trailing slashes + if (channelEndpoint.endsWith("/")) + { + channelEndpoint = channelEndpoint.substring(0, channelEndpoint.length() - 1); + } + return channelEndpoint; + } +} http://git-wip-us.apache.org/repos/asf/flex-blazeds/blob/8315f8fa/common/src/main/java/flex/messaging/config/ClientConfiguration.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/flex/messaging/config/ClientConfiguration.java b/common/src/main/java/flex/messaging/config/ClientConfiguration.java new file mode 100644 index 0000000..df6f03f --- /dev/null +++ b/common/src/main/java/flex/messaging/config/ClientConfiguration.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package flex.messaging.config; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * + */ +public class ClientConfiguration implements ServicesConfiguration +{ + protected final Map channelSettings; + protected final List defaultChannels; + protected final List serviceSettings; + protected LoggingSettings loggingSettings; + protected Map configPaths; + protected final Map clusterSettings; + protected FlexClientSettings flexClientSettings; + + public ClientConfiguration() + { + channelSettings = new HashMap(); + defaultChannels = new ArrayList(4); + clusterSettings = new HashMap(); + serviceSettings = new ArrayList(); + configPaths = new HashMap(); + } + + /* + * CHANNEL CONFIGURATION + */ + + public void addChannelSettings(String id, ChannelSettings settings) + { + channelSettings.put(id, settings); + } + + public ChannelSettings getChannelSettings(String ref) + { + return (ChannelSettings)channelSettings.get(ref); + } + + public Map getAllChannelSettings() + { + return channelSettings; + } + + /* + * DEFAULT CHANNELS CONFIGURATION + */ + public void addDefaultChannel(String id) + { + defaultChannels.add(id); + } + + public List getDefaultChannels() + { + return defaultChannels; + } + + /* + * SERVICE CONFIGURATION + */ + + public void addServiceSettings(ServiceSettings settings) + { + serviceSettings.add(settings); + } + + public ServiceSettings getServiceSettings(String serviceType) + { + for (Iterator iter = serviceSettings.iterator(); iter.hasNext();) + { + ServiceSettings serviceSettings = (ServiceSettings) iter.next(); + if (serviceSettings.getId().equals(serviceType)) + return serviceSettings; + } + return null; + } + + public List getAllServiceSettings() + { + return serviceSettings; + } + + /* + * CLUSTER CONFIGURATION + */ + + public void addClusterSettings(ClusterSettings settings) + { + if (settings.isDefault()) + { + for (Iterator it = clusterSettings.values().iterator(); it.hasNext(); ) + { + ClusterSettings cs = (ClusterSettings) it.next(); + + if (cs.isDefault()) + { + ConfigurationException cx = new ConfigurationException(); + cx.setMessage(10214, new Object[] { settings.getClusterName(), cs.getClusterName() }); + throw cx; + } + } + } + if (clusterSettings.containsKey(settings.getClusterName())) + { + ConfigurationException cx = new ConfigurationException(); + cx.setMessage(10206, new Object[] { settings.getClusterName() }); + throw cx; + } + clusterSettings.put(settings.getClusterName(), settings); + } + + public ClusterSettings getClusterSettings(String clusterId) + { + for (Iterator it = clusterSettings.values().iterator(); it.hasNext(); ) + { + ClusterSettings cs = (ClusterSettings) it.next(); + if (cs.getClusterName() == null && clusterId == null) + return cs; // handle null case + if (cs.getClusterName() != null && cs.getClusterName().equals(clusterId)) + return cs; + } + return null; + } + + public ClusterSettings getDefaultCluster() + { + for (Iterator it = clusterSettings.values().iterator(); it.hasNext(); ) + { + ClusterSettings cs = (ClusterSettings) it.next(); + if (cs.isDefault()) + return cs; + } + return null; + } + + /* + * LOGGING CONFIGURATION + */ + public void setLoggingSettings(LoggingSettings settings) + { + loggingSettings = settings; + } + + public LoggingSettings getLoggingSettings() + { + return loggingSettings; + } + + + public void addConfigPath(String path, long modified) + { + configPaths.put(path, new Long(modified)); + } + + public Map getConfigPaths() + { + return configPaths; + } + + public void setFlexClientSettings(FlexClientSettings value) + { + flexClientSettings = value; + } + + public FlexClientSettings getFlexClientSettings() + { + return flexClientSettings; + } + +}
