http://git-wip-us.apache.org/repos/asf/flex-blazeds/blob/bf2e1dc9/core/src/flex/messaging/config/ServerConfigurationParser.java ---------------------------------------------------------------------- diff --git a/core/src/flex/messaging/config/ServerConfigurationParser.java b/core/src/flex/messaging/config/ServerConfigurationParser.java new file mode 100644 index 0000000..9ff3e1c --- /dev/null +++ b/core/src/flex/messaging/config/ServerConfigurationParser.java @@ -0,0 +1,1920 @@ +/* + * 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.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.StringTokenizer; + +import flex.messaging.config.ThrottleSettings.Policy; +import flex.messaging.util.LocaleUtils; + +/** + * Processes 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 abstract class ServerConfigurationParser extends AbstractConfigurationParser +{ + /** + * Used to verify that advanced messaging support has been registered if necessary. + * If other configuration requires it, but it was not registered a ConfigurationException is thrown. + */ + private boolean verifyAdvancedMessagingSupport = false; + private boolean advancedMessagingSupportRegistered = false; + + @Override + protected void parseTopLevelConfig(Document doc) + { + Node root = selectSingleNode(doc, "/" + SERVICES_CONFIG_ELEMENT); + + if (root != null) + { + allowedChildElements(root, SERVICES_CONFIG_CHILDREN); + + securitySection(root); // Parse security before channels. + + serversSection(root); + + channelsSection(root); + + services(root); + + clusters(root); + + logging(root); + + system(root); + + flexClient(root); + + factories(root); + + messageFilters(root); + + validators(root); + + // Validate that any dependencies on advanced messaging support can be satisified at runtime. + if (verifyAdvancedMessagingSupport && !advancedMessagingSupportRegistered) + { + ConfigurationException e = new ConfigurationException(); + e.setMessage(REQUIRE_ADVANCED_MESSAGING_SUPPORT); + throw e; + } + } + else + { + // The services configuration root element must be '{SERVICES_CONFIG_ELEMENT}'. + ConfigurationException e = new ConfigurationException(); + e.setMessage(INVALID_SERVICES_ROOT, new Object[]{SERVICES_CONFIG_ELEMENT}); + throw e; + } + } + + private void clusters(Node root) + { + Node clusteringNode = selectSingleNode(root, CLUSTERS_ELEMENT); + if (clusteringNode != null) + { + allowedAttributesOrElements(clusteringNode, CLUSTERING_CHILDREN); + + NodeList clusters = selectNodeList(clusteringNode, CLUSTER_DEFINITION_ELEMENT); + for (int i = 0; i < clusters.getLength(); i++) + { + Node cluster = clusters.item(i); + requiredAttributesOrElements(cluster, CLUSTER_DEFINITION_CHILDREN); + String clusterName = getAttributeOrChildElement(cluster, ID_ATTR); + if (!isValidID(clusterName)) + continue; + + String propsFileName = getAttributeOrChildElement(cluster, CLUSTER_PROPERTIES_ATTR); + ClusterSettings clusterSettings = new ClusterSettings(); + clusterSettings.setClusterName(clusterName); + clusterSettings.setPropsFileName(propsFileName); + String className = getAttributeOrChildElement(cluster, CLASS_ATTR); + if (className != null && className.length() > 0) + clusterSettings.setImplementationClass(className); + + String defaultValue = getAttributeOrChildElement(cluster, ClusterSettings.DEFAULT_ELEMENT); + if (defaultValue != null && defaultValue.length() > 0) + { + if (defaultValue.equalsIgnoreCase(TRUE_STRING)) + clusterSettings.setDefault(true); + else if (!defaultValue.equalsIgnoreCase(FALSE_STRING)) + { + ConfigurationException e = new ConfigurationException(); + e.setMessage(10215, new Object[] {clusterName, defaultValue}); + throw e; + } + } + String ulb = getAttributeOrChildElement(cluster, ClusterSettings.URL_LOAD_BALANCING); + if (ulb != null && ulb.length() > 0) + { + if (ulb.equalsIgnoreCase(FALSE_STRING)) + { + clusterSettings.setURLLoadBalancing(false); + } + else if (!ulb.equalsIgnoreCase(TRUE_STRING)) + { + ConfigurationException e = new ConfigurationException(); + e.setMessage(10216, new Object[] {clusterName, ulb}); + throw e; + } + } + + NodeList properties = selectNodeList(cluster, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(cluster)); + clusterSettings.addProperties(map); + } + + ((MessagingConfiguration)config).addClusterSettings(clusterSettings); + + } + } + } + + + private void securitySection(Node root) + { + Node security = selectSingleNode(root, SECURITY_ELEMENT); + + if (security == null) + return; + + allowedChildElements(security, SECURITY_CHILDREN); + + NodeList list = selectNodeList(security, SECURITY_CONSTRAINT_DEFINITION_ELEMENT); + for (int i = 0; i < list.getLength(); i++) + { + Node constraint = list.item(i); + securityConstraint(constraint, false); + } + + list = selectNodeList(security, CONSTRAINT_INCLUDE_ELEMENT); + for (int i = 0; i < list.getLength(); i++) + { + Node include = list.item(i); + securityConstraintInclude(include); + } + + list = selectNodeList(security, LOGIN_COMMAND_ELEMENT); + for (int i = 0; i < list.getLength(); i++) + { + Node login = list.item(i); + LoginCommandSettings loginCommandSettings= new LoginCommandSettings(); + requiredAttributesOrElements(login, LOGIN_COMMAND_REQ_CHILDREN); + allowedAttributesOrElements(login, LOGIN_COMMAND_CHILDREN); + + String server = getAttributeOrChildElement(login, SERVER_ATTR); + if (server.length() == 0) + { + // Attribute '{SERVER_ATTR}' must be specified for element '{LOGIN_COMMAND_ELEMENT}' + ConfigurationException e = new ConfigurationException(); + e.setMessage(MISSING_ATTRIBUTE, new Object[]{SERVER_ATTR, LOGIN_COMMAND_ELEMENT}); + throw e; + } + loginCommandSettings.setServer(server); + + String loginClass = getAttributeOrChildElement(login, CLASS_ATTR); + if (loginClass.length() == 0) + { + // Attribute '{CLASS_ATTR}' must be specified for element '{LOGIN_COMMAND_ELEMENT}' + ConfigurationException e = new ConfigurationException(); + e.setMessage(MISSING_ATTRIBUTE, new Object[]{CLASS_ATTR, LOGIN_COMMAND_ELEMENT}); + throw e; + } + loginCommandSettings.setClassName(loginClass); + + boolean isPerClientAuth = Boolean.valueOf(getAttributeOrChildElement(login, PER_CLIENT_AUTH)); + loginCommandSettings.setPerClientAuthentication(isPerClientAuth); + + ((MessagingConfiguration)config).getSecuritySettings().addLoginCommandSettings(loginCommandSettings); + } + + boolean recreateHttpSessionAfterLogin = Boolean.valueOf(getAttributeOrChildElement(security, RECREATE_HTTPSESSION_AFTER_LOGIN_ELEMENT)); + ((MessagingConfiguration)config).getSecuritySettings().setRecreateHttpSessionAfterLogin(recreateHttpSessionAfterLogin); + } + + private SecurityConstraint securityConstraint(Node constraint, boolean inline) + { + SecurityConstraint sc; + + // Validation + allowedAttributesOrElements(constraint, SECURITY_CONSTRAINT_DEFINITION_CHILDREN); + + // Constraint by reference + String ref = getAttributeOrChildElement(constraint, REF_ATTR); + if (ref.length() > 0) + { + allowedAttributesOrElements(constraint, new String[] {REF_ATTR}); + + sc = ((MessagingConfiguration)config).getSecuritySettings().getConstraint(ref); + if (sc == null) + { + // {SECURITY_CONSTRAINT_DEFINITION_ELEMENT} not found for reference '{ref}'. + ConfigurationException e = new ConfigurationException(); + e.setMessage(REF_NOT_FOUND, new Object[]{SECURITY_CONSTRAINT_DEFINITION_ELEMENT, ref}); + throw e; + } + } + else + { + // New security constraint + String id = getAttributeOrChildElement(constraint, ID_ATTR); + + // If not inline, we must have a valid id to register the constraint! + if (inline) + { + sc = new SecurityConstraint(""); + } + else if (isValidID(id)) + { + sc = new SecurityConstraint(id); + ((MessagingConfiguration)config).getSecuritySettings().addConstraint(sc); + } + else + { + //Invalid {SECURITY_CONSTRAINT_DEFINITION_ELEMENT} id '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_ID, new Object[]{SECURITY_CONSTRAINT_DEFINITION_ELEMENT, id}); + ex.setDetails(INVALID_ID); + throw ex; + } + + // Authentication Method + String method = getAttributeOrChildElement(constraint, AUTH_METHOD_ELEMENT); + sc.setMethod(method); + + // Roles + Node rolesNode = selectSingleNode(constraint, ROLES_ELEMENT); + if (rolesNode != null) + { + allowedChildElements(rolesNode, ROLES_CHILDREN); + NodeList roles = selectNodeList(rolesNode, ROLE_ELEMENT); + for (int r = 0; r < roles.getLength(); r++) + { + Node roleNode = roles.item(r); + String role = evaluateExpression(roleNode, ".").toString().trim(); + if (role.length() > 0) + { + sc.addRole(role); + } + } + } + } + + return sc; + } + + private void securityConstraintInclude(Node constraintInclude) + { + // Validation + allowedAttributesOrElements(constraintInclude, CONSTRAINT_INCLUDE_CHILDREN); + + String src = getAttributeOrChildElement(constraintInclude, SRC_ATTR); + String dir = getAttributeOrChildElement(constraintInclude, DIRECTORY_ATTR); + if (src.length() > 0) + { + constraintIncludeFile(src); + } + else if (dir.length() > 0) + { + constraintIncludeDirectory(dir); + } + else + { + // The include element ''{0}'' must specify either the ''{1}'' or ''{2}'' attribute. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(MISSING_INCLUDE_ATTRIBUTES, new Object[]{constraintInclude.getNodeName(), SRC_ATTR, DIRECTORY_ATTR}); + throw ex; + } + } + + private void constraintIncludeFile(String src) + { + Document doc = loadDocument(src, fileResolver.getIncludedFile(src)); + doc.getDocumentElement().normalize(); + + // Check for <security-constraints> wrapping more than one definition + Node servicesNode = selectSingleNode(doc, SECURITY_CONSTRAINTS_ELEMENT); + if (servicesNode != null) + { + allowedChildElements(servicesNode, SECURITY_CONSTRAINTS_CHILDREN); + NodeList constraints = selectNodeList(servicesNode, SECURITY_CONSTRAINT_ELEMENT); + for (int a = 0; a < constraints.getLength(); a++) + { + Node constraint = constraints.item(a); + securityConstraint(constraint, false); + } + fileResolver.popIncludedFile(); + } + else // Check for single <security-constraint> + { + Node constraint = selectSingleNode(doc, "/" + SECURITY_CONSTRAINT_ELEMENT); + if (constraint != null) + { + securityConstraint(constraint, false); + fileResolver.popIncludedFile(); + } + else + { + // The {0} root element in file {1} must be ''{2}'' or ''{3}''. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_INCLUDE_ROOT, new Object[]{CONSTRAINT_INCLUDE_ELEMENT, src, SECURITY_CONSTRAINTS_ELEMENT, SECURITY_CONSTRAINT_ELEMENT}); + throw ex; + } + } + } + + private void constraintIncludeDirectory(String dir) + { + List files = fileResolver.getFiles(dir); + for (int i = 0; i < files.size(); i++) + { + String src = (String) files.get(i); + constraintIncludeFile(src); + } + } + + private void serversSection(Node root) + { + // Only MessagingConfiguration supports the servers element configuration. + // The general ServicesConfiguration interface does not. + if (!(config instanceof MessagingConfiguration)) + return; + + Node serversNode = selectSingleNode(root, SERVERS_ELEMENT); + if (serversNode != null) + { + // Validation + allowedAttributesOrElements(serversNode, SERVERS_CHILDREN); + + NodeList servers = selectNodeList(serversNode, SERVER_ELEMENT); + for (int i = 0; i < servers.getLength(); i++) + { + Node server = servers.item(i); + serverDefinition(server); + } + } + } + + private void serverDefinition(Node server) + { + // Validation + requiredAttributesOrElements(server, SERVER_REQ_CHILDREN); + allowedAttributesOrElements(server, SERVER_CHILDREN); + + String id = getAttributeOrChildElement(server, ID_ATTR); + if (isValidID(id)) + { + SharedServerSettings settings = new SharedServerSettings(); + settings.setId(id); + settings.setSourceFile(getSourceFileOf(server)); + String className = getAttributeOrChildElement(server, CLASS_ATTR); + if (className.length() > 0) + { + settings.setClassName(className); + // Custom server properties. + NodeList properties = selectNodeList(server, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(server)); + settings.addProperties(map); + } + ((MessagingConfiguration)config).addSharedServerSettings(settings); + } + else + { + // Class not specified for {MESSAGE_FILTER_ELEMENT} '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(CLASS_NOT_SPECIFIED, new Object[]{SERVER_ELEMENT, id}); + throw ex; + } + } + } + + private void channelsSection(Node root) + { + Node channelsNode = selectSingleNode(root, CHANNELS_ELEMENT); + if (channelsNode != null) + { + // Validation + allowedAttributesOrElements(channelsNode, CHANNELS_CHILDREN); + + NodeList channels = selectNodeList(channelsNode, CHANNEL_DEFINITION_ELEMENT); + for (int i = 0; i < channels.getLength(); i++) + { + Node channel = channels.item(i); + channelDefinition(channel); + } + + NodeList includes = selectNodeList(channelsNode, CHANNEL_INCLUDE_ELEMENT); + for (int i = 0; i < includes.getLength(); i++) + { + Node include = includes.item(i); + channelInclude(include); + } + } + } + + private void channelDefinition(Node channel) + { + // Validation + requiredAttributesOrElements(channel, CHANNEL_DEFINITION_REQ_CHILDREN); + allowedAttributesOrElements(channel, CHANNEL_DEFINITION_CHILDREN); + + String id = getAttributeOrChildElement(channel, ID_ATTR); + if (isValidID(id)) + { + // Don't allow multiple channels with the same id + if (config.getChannelSettings(id) != null) + { + // Cannot have multiple channels with the same id ''{0}''. + ConfigurationException e = new ConfigurationException(); + e.setMessage(DUPLICATE_CHANNEL_ERROR, new Object[]{id}); + throw e; + } + + ChannelSettings channelSettings = new ChannelSettings(id); + channelSettings.setSourceFile(getSourceFileOf(channel)); + + // Note whether the channel-definition was for a remote endpoint + String remote = getAttributeOrChildElement(channel, REMOTE_ATTR); + channelSettings.setRemote(Boolean.valueOf(remote)); + + // Endpoint + Node endpoint = selectSingleNode(channel, ENDPOINT_ELEMENT); + if (endpoint != null) + { + // Endpoint Validation + allowedAttributesOrElements(endpoint, ENDPOINT_CHILDREN); + + String type = getAttributeOrChildElement(endpoint, CLASS_ATTR); + channelSettings.setEndpointType(type); + + // The url attribute may also be specified by the deprecated uri attribute + String uri = getAttributeOrChildElement(endpoint, URL_ATTR); + if (uri == null || EMPTY_STRING.equals(uri)) + uri = getAttributeOrChildElement(endpoint, URI_ATTR); + channelSettings.setUri(uri); + + config.addChannelSettings(id, channelSettings); + } + + channelServerOnlyAttribute(channel, channelSettings); + + // Server reference + Node server = selectSingleNode(channel, SERVER_ELEMENT); + if (server != null) + { + requiredAttributesOrElements(server, CHANNEL_DEFINITION_SERVER_REQ_CHILDREN); + + String serverId = getAttributeOrChildElement(server, REF_ATTR); + channelSettings.setServerId(serverId); + } + + // Channel Properties + NodeList properties = selectNodeList(channel, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(channel)); + channelSettings.addProperties(map); + + // Sniff for adaptive-frequency under flex-client-queue-processor hich requires advanced messaging support. + if (!verifyAdvancedMessagingSupport) + { + ConfigMap outboundQueueProcessor = map.getPropertyAsMap(FLEX_CLIENT_OUTBOUND_QUEUE_PROCESSOR_ELEMENT, null); + if (outboundQueueProcessor != null) + { + // Flex client queue processor properties + ConfigMap queueProcessorProperties = outboundQueueProcessor.getPropertyAsMap(PROPERTIES_ELEMENT, null); + if (queueProcessorProperties != null) + { + // Sniff for adaptive-frequency which requires advanced messaging support. + boolean adaptiveFrequency = queueProcessorProperties.getPropertyAsBoolean(ADAPTIVE_FREQUENCY, false); + if (adaptiveFrequency) + verifyAdvancedMessagingSupport = true; + } + } + } + } + + // Channel Security + + // Security-constraint short-cut attribute + String ref = evaluateExpression(channel, "@" + SECURITY_CONSTRAINT_ATTR).toString().trim(); + if (ref.length() > 0) + { + SecurityConstraint sc = ((MessagingConfiguration)config).getSecuritySettings().getConstraint(ref); + if (sc != null) + { + channelSettings.setConstraint(sc); + } + else + { + // {SECURITY_CONSTRAINT_ELEMENT} not found for reference '{ref}' in channel '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(REF_NOT_FOUND_IN_CHANNEL, new Object[]{SECURITY_CONSTRAINT_ATTR, ref, id}); + throw ex; + } + } + else + { + // Inline security element + Node security = selectSingleNode(channel, SECURITY_ELEMENT); + if (security != null) + { + allowedChildElements(security, EMBEDDED_SECURITY_CHILDREN); + Node constraint = selectSingleNode(security, SECURITY_CONSTRAINT_ELEMENT); + if (constraint != null) + { + SecurityConstraint sc = securityConstraint(constraint, true); + channelSettings.setConstraint(sc); + } + } + } + } + else + { + // Invalid {CHANNEL_DEFINITION_ELEMENT} id '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_ID, new Object[]{CHANNEL_DEFINITION_ELEMENT, id}); + ex.setDetails(INVALID_ID); + throw ex; + } + } + + private void channelServerOnlyAttribute(Node channel, ChannelSettings channelSettings) + { + String clientType = getAttributeOrChildElement(channel, CLASS_ATTR); + clientType = clientType.length() > 0? clientType : null; + + String serverOnlyString = getAttributeOrChildElement(channel, SERVER_ONLY_ATTR); + boolean serverOnly = serverOnlyString.length() > 0? Boolean.valueOf(serverOnlyString) : false; + + if (clientType == null && !serverOnly) // None set. + { + String url = channelSettings.getUri(); + boolean serverOnlyProtocol = (url.startsWith("samfsocket") || url.startsWith("amfsocket") || url.startsWith("ws")); + if (!serverOnlyProtocol) + { + // Endpoint ''{0}'' needs to have either class or server-only attribute defined. + ConfigurationException ce = new ConfigurationException(); + ce.setMessage(CLASS_OR_SERVER_ONLY_ERROR, new Object[]{channelSettings.getId()}); + throw ce; + } + channelSettings.setServerOnly(true); + } + else if (clientType != null && serverOnly) // Both set. + { + // Endpoint ''{0}'' cannot have both class and server-only attribute defined. + ConfigurationException ce = new ConfigurationException(); + ce.setMessage(CLASS_AND_SERVER_ONLY_ERROR, new Object[]{channelSettings.getId()}); + throw ce; + } + else // One of them set. + { + if (serverOnly) + channelSettings.setServerOnly(true); + else + channelSettings.setClientType(clientType); + } + } + + private void channelInclude(Node channelInclude) + { + // Validation + allowedAttributesOrElements(channelInclude, CHANNEL_INCLUDE_CHILDREN); + + String src = getAttributeOrChildElement(channelInclude, SRC_ATTR); + String dir = getAttributeOrChildElement(channelInclude, DIRECTORY_ATTR); + if (src.length() > 0) + { + channelIncludeFile(src); + } + else if (dir.length() > 0) + { + channelIncludeDirectory(dir); + } + else + { + // The include element ''{0}'' must specify either the ''{1}'' or ''{2}'' attribute. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(MISSING_INCLUDE_ATTRIBUTES, new Object[]{channelInclude.getNodeName(), SRC_ATTR, DIRECTORY_ATTR}); + throw ex; + } + } + + private void channelIncludeFile(String src) + { + Document doc = loadDocument(src, fileResolver.getIncludedFile(src)); + doc.getDocumentElement().normalize(); + + // Check for multiple channels in a single file. + Node channelsNode = selectSingleNode(doc, CHANNELS_ELEMENT); + if (channelsNode != null) + { + allowedChildElements(channelsNode, CHANNELS_CHILDREN); + NodeList channels = selectNodeList(channelsNode, CHANNEL_DEFINITION_ELEMENT); + for (int a = 0; a < channels.getLength(); a++) + { + Node service = channels.item(a); + channelDefinition(service); + } + fileResolver.popIncludedFile(); + } + else // Check for single channel in the file. + { + Node channel = selectSingleNode(doc, "/" + CHANNEL_DEFINITION_ELEMENT); + if (channel != null) + { + channelDefinition(channel); + fileResolver.popIncludedFile(); + } + else + { + // The {0} root element in file {1} must be '{CHANNELS_ELEMENT}' or '{CHANNEL_ELEMENT}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_INCLUDE_ROOT, new Object[]{CHANNEL_INCLUDE_ELEMENT, src, CHANNELS_ELEMENT, CHANNEL_DEFINITION_ELEMENT}); + throw ex; + } + } + } + + private void channelIncludeDirectory(String dir) + { + List files = fileResolver.getFiles(dir); + for (int i = 0; i < files.size(); i++) + { + String src = (String) files.get(i); + channelIncludeFile(src); + } + } + + private void services(Node root) + { + Node servicesNode = selectSingleNode(root, SERVICES_ELEMENT); + if (servicesNode != null) + { + // Validation + allowedChildElements(servicesNode, SERVICES_CHILDREN); + + // Default Channels for the application + Node defaultChannels = selectSingleNode(servicesNode, DEFAULT_CHANNELS_ELEMENT); + if (defaultChannels != null) + { + allowedChildElements(defaultChannels, DEFAULT_CHANNELS_CHILDREN); + NodeList channels = selectNodeList(defaultChannels, CHANNEL_ELEMENT); + for (int c = 0; c < channels.getLength(); c++) + { + Node chan = channels.item(c); + allowedAttributes(chan, new String[] {REF_ATTR}); + defaultChannel(chan); + } + } + + // Service Includes + NodeList services = selectNodeList(servicesNode, SERVICE_INCLUDE_ELEMENT); + for (int i = 0; i < services.getLength(); i++) + { + Node service = services.item(i); + serviceInclude(service); + } + + // Service + services = selectNodeList(servicesNode, SERVICE_ELEMENT); + for (int i = 0; i < services.getLength(); i++) + { + Node service = services.item(i); + service(service); + } + } + } + + private void serviceInclude(Node serviceInclude) + { + // Validation + allowedAttributesOrElements(serviceInclude, SERVICE_INCLUDE_CHILDREN); + + String src = getAttributeOrChildElement(serviceInclude, SRC_ATTR); + String dir = getAttributeOrChildElement(serviceInclude, DIRECTORY_ATTR); + if (src.length() > 0) + { + serviceIncludeFile(src); + } + else if (dir.length() > 0) + { + serviceIncludeDirectory(dir); + } + else + { + // The include element ''{0}'' must specify either the ''{1}'' or ''{2}'' attribute. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(MISSING_INCLUDE_ATTRIBUTES, new Object[]{serviceInclude.getNodeName(), SRC_ATTR, DIRECTORY_ATTR}); + throw ex; + } + } + + private void serviceIncludeFile(String src) + { + Document doc = loadDocument(src, fileResolver.getIncludedFile(src)); + doc.getDocumentElement().normalize(); + + // Check for multiple services defined in a <services> tag + Node servicesNode = selectSingleNode(doc, SERVICES_ELEMENT); + if (servicesNode != null) + { + allowedChildElements(servicesNode, SERVICES_CHILDREN); + NodeList services = selectNodeList(servicesNode, SERVICE_ELEMENT); + for (int a = 0; a < services.getLength(); a++) + { + Node service = services.item(a); + service(service); + } + fileResolver.popIncludedFile(); + } + else // Check for single <service> definition. + { + Node service = selectSingleNode(doc, "/" + SERVICE_ELEMENT); + if (service != null) + { + service(service); + fileResolver.popIncludedFile(); + } + else + { + // The {0} root element in file {1} must be ''{2}'' or ''{3}''. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_INCLUDE_ROOT, new Object[]{SERVICE_INCLUDE_ELEMENT, src, SERVICES_ELEMENT, SERVICE_ELEMENT}); + throw ex; + } + } + } + + private void serviceIncludeDirectory(String dir) + { + List files = fileResolver.getFiles(dir); + for (int i = 0; i < files.size(); i++) + { + String src = (String) files.get(i); + serviceIncludeFile(src); + } + } + + private void service(Node service) + { + // Validation + requiredAttributesOrElements(service, SERVICE_REQ_CHILDREN); + allowedAttributesOrElements(service, SERVICE_CHILDREN); + + String id = getAttributeOrChildElement(service, ID_ATTR); + if (isValidID(id)) + { + ServiceSettings serviceSettings = config.getServiceSettings(id); + if (serviceSettings == null) + { + serviceSettings = new ServiceSettings(id); + serviceSettings.setSourceFile(getSourceFileOf(service)); + config.addServiceSettings(serviceSettings); + } + else + { + // Duplicate service definition '{0}'. + ConfigurationException e = new ConfigurationException(); + e.setMessage(DUPLICATE_SERVICE_ERROR, new Object[]{id}); + throw e; + } + + // Service Class Name + String className = getAttributeOrChildElement(service, CLASS_ATTR); + if (className.length() > 0) + { + serviceSettings.setClassName(className); + + // Sniff for AdvancedMessagingSupport. + if (className.equals("flex.messaging.services.AdvancedMessagingSupport")) + advancedMessagingSupportRegistered = true; + } + else + { + // Class not specified for {SERVICE_ELEMENT} '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(CLASS_NOT_SPECIFIED, new Object[]{SERVICE_ELEMENT, id}); + throw ex; + } + + // Service Message Types - deprecated + + // Service Properties + NodeList properties = selectNodeList(service, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(service)); + serviceSettings.addProperties(map); + } + + // Default Channels + Node defaultChannels = selectSingleNode(service, DEFAULT_CHANNELS_ELEMENT); + if (defaultChannels != null) + { + allowedChildElements(defaultChannels, DEFAULT_CHANNELS_CHILDREN); + NodeList channels = selectNodeList(defaultChannels, CHANNEL_ELEMENT); + for (int c = 0; c < channels.getLength(); c++) + { + Node chan = channels.item(c); + allowedAttributes(chan, new String[] {REF_ATTR}); + defaultChannel(chan, serviceSettings); + } + } + // Fall back on application's default channels + else if (config.getDefaultChannels().size() > 0) + { + for (Iterator iter = config.getDefaultChannels().iterator(); iter.hasNext();) + { + String channelId = (String)iter.next(); + ChannelSettings channel = config.getChannelSettings(channelId); + serviceSettings.addDefaultChannel(channel); + } + } + + // Default Security Constraint + Node defaultSecurityConstraint = selectSingleNode(service, DEFAULT_SECURITY_CONSTRAINT_ELEMENT); + if (defaultSecurityConstraint != null) + { + // Validation + requiredAttributesOrElements(defaultSecurityConstraint, new String[] {REF_ATTR}); + allowedAttributesOrElements(defaultSecurityConstraint, new String[] {REF_ATTR}); + + String ref = getAttributeOrChildElement(defaultSecurityConstraint, REF_ATTR); + if (ref.length() > 0) + { + SecurityConstraint sc = ((MessagingConfiguration)config).getSecuritySettings().getConstraint(ref); + if (sc == null) + { + // {SECURITY_CONSTRAINT_DEFINITION_ELEMENT} not found for reference '{ref}'. + ConfigurationException e = new ConfigurationException(); + e.setMessage(REF_NOT_FOUND, new Object[]{SECURITY_CONSTRAINT_DEFINITION_ELEMENT, ref}); + throw e; + } + serviceSettings.setConstraint(sc); + } + else + { + //Invalid default-security-constraint reference ''{0}'' in service ''{1}''. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_SECURITY_CONSTRAINT_REF, new Object[]{ref, id}); + throw ex; + } + } + + // Adapter Definitions + Node adapters = selectSingleNode(service, ADAPTERS_ELEMENT); + if (adapters != null) + { + allowedChildElements(adapters, ADAPTERS_CHILDREN); + NodeList serverAdapters = selectNodeList(adapters, ADAPTER_DEFINITION_ELEMENT); + for (int a = 0; a < serverAdapters.getLength(); a++) + { + Node adapter = serverAdapters.item(a); + adapterDefinition(adapter, serviceSettings); + } + NodeList adapterIncludes = selectNodeList(adapters, ADAPTER_INCLUDE_ELEMENT); + for (int a = 0; a < adapterIncludes.getLength(); a++) + { + Node include = adapterIncludes.item(a); + adapterInclude(include, serviceSettings); + } + } + + // Destinations + NodeList list = selectNodeList(service, DESTINATION_ELEMENT); + for (int i = 0; i < list.getLength(); i++) + { + Node dest = list.item(i); + destination(dest, serviceSettings); + } + + // Destination Includes + list = selectNodeList(service, DESTINATION_INCLUDE_ELEMENT); + for (int i = 0; i < list.getLength(); i++) + { + Node dest = list.item(i); + destinationInclude(dest, serviceSettings); + } + } + else + { + //Invalid {SERVICE_ELEMENT} id '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_ID, new Object[]{SERVICE_ELEMENT, id}); + throw ex; + } + } + + /** + * A Flex application can declare default channels for its services. If a + * service specifies its own list of channels it overrides these defaults. + * <p> + * <default-channels><br/> + * <channel ref="channel-id"/><br/> + * <default-channels> + * </p> + * @param chan the channel node + */ + private void defaultChannel(Node chan) + { + String ref = getAttributeOrChildElement(chan, REF_ATTR); + + if (ref.length() > 0) + { + ChannelSettings channel = config.getChannelSettings(ref); + if (channel != null) + { + config.addDefaultChannel(channel.getId()); + } + else + { + // {0} not found for reference '{1}' + ConfigurationException e = new ConfigurationException(); + e.setMessage(REF_NOT_FOUND, new Object[]{CHANNEL_ELEMENT, ref}); + throw e; + } + } + else + { + //A default channel was specified without a reference for service '{0}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_DEFAULT_CHANNEL, new Object[]{"MessageBroker"}); + throw ex; + } + } + + /** + * A service can declare default channels for its destinations. If a destination + * specifies its own list of channels it overrides these defaults. + * <p> + * <default-channels><br/> + * <channel ref="channel-id"/><br/> + * <default-channels> + * </p> + * @param chan the channel node + * @param serviceSettings service settings + */ + private void defaultChannel(Node chan, ServiceSettings serviceSettings) + { + String ref = getAttributeOrChildElement(chan, REF_ATTR); + + if (ref.length() > 0) + { + ChannelSettings channel = config.getChannelSettings(ref); + if (channel != null) + { + serviceSettings.addDefaultChannel(channel); + } + else + { + // {0} not found for reference '{1}' + ConfigurationException e = new ConfigurationException(); + e.setMessage(REF_NOT_FOUND, new Object[]{CHANNEL_ELEMENT, ref}); + throw e; + } + } + else + { + //A default channel was specified without a reference for service '{0}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_DEFAULT_CHANNEL, new Object[]{serviceSettings.getId()}); + throw ex; + } + } + + private void adapterDefinition(Node adapter, ServiceSettings serviceSettings) + { + // Validation + requiredAttributesOrElements(adapter, ADAPTER_DEFINITION_REQ_CHILDREN); + allowedChildElements(adapter, ADAPTER_DEFINITION_CHILDREN); + + String serviceId = serviceSettings.getId(); + + String id = getAttributeOrChildElement(adapter, ID_ATTR); + if (isValidID(id)) + { + AdapterSettings adapterSettings = new AdapterSettings(id); + adapterSettings.setSourceFile(getSourceFileOf(adapter)); + String className = getAttributeOrChildElement(adapter, CLASS_ATTR); + + if (className.length() > 0) + { + adapterSettings.setClassName(className); + + // Default Adapter Check + boolean isDefault = Boolean.valueOf(getAttributeOrChildElement(adapter, DEFAULT_ATTR)); + if (isDefault) + { + adapterSettings.setDefault(isDefault); + + AdapterSettings defaultAdapter; + defaultAdapter = serviceSettings.getDefaultAdapter(); + + if (defaultAdapter != null) + { + // Duplicate default adapter '{0}' in service '{1}'. '{2}' has already been selected as the default. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(DUPLICATE_DEFAULT_ADAPTER, new Object[]{id, serviceId, defaultAdapter.getId()}); + throw ex; + } + } + + serviceSettings.addAdapterSettings(adapterSettings); + + // Adapter Properties + NodeList properties = selectNodeList(adapter, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(adapter)); + adapterSettings.addProperties(map); + } + } + else + { + // Class not specified for {ADAPTER_DEFINITION_ELEMENT} '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(CLASS_NOT_SPECIFIED, new Object[]{ADAPTER_DEFINITION_ELEMENT, id}); + throw ex; + } + } + else + { + //Invalid {ADAPTER_DEFINITION_ELEMENT} id '{id}' for service '{serviceId}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_ID_IN_SERVICE, new Object[]{ADAPTER_DEFINITION_ELEMENT, id, serviceId}); + throw ex; + } + } + + private void adapterInclude(Node adapterInclude, ServiceSettings serviceSettings) + { + // Validation + allowedAttributesOrElements(adapterInclude, ADAPTER_INCLUDE_CHILDREN); + + String src = getAttributeOrChildElement(adapterInclude, SRC_ATTR); + String dir = getAttributeOrChildElement(adapterInclude, DIRECTORY_ATTR); + if (src.length() > 0) + { + adapterIncludeFile(serviceSettings, src); + } + else if (dir.length() > 0) + { + adapterIncludeDirectory(serviceSettings, dir); + } + else + { + // Attribute '{0}' must be specified for element '{1}' + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(MISSING_INCLUDE_ATTRIBUTES, new Object[]{adapterInclude.getNodeName(), SRC_ATTR, DIRECTORY_ATTR}); + throw ex; + } + } + + private void adapterIncludeDirectory(ServiceSettings serviceSettings, String dir) + { + List files = fileResolver.getFiles(dir); + for (int i = 0; i < files.size(); i++) + { + String src = (String) files.get(i); + adapterIncludeFile(serviceSettings, src); + } + } + + private void adapterIncludeFile(ServiceSettings serviceSettings, String src) + { + Document doc = loadDocument(src, fileResolver.getIncludedFile(src)); + doc.getDocumentElement().normalize(); + + // Check for multiple adapters defined in file wrapped in an <adapters> element + Node adaptersNode = selectSingleNode(doc, ADAPTERS_ELEMENT); + if (adaptersNode != null) + { + allowedChildElements(adaptersNode, ADAPTERS_CHILDREN); + NodeList adapters = selectNodeList(adaptersNode, ADAPTER_DEFINITION_ELEMENT); + for (int a = 0; a < adapters.getLength(); a++) + { + Node adapter = adapters.item(a); + adapterDefinition(adapter, serviceSettings); + } + fileResolver.popIncludedFile(); + } + else // Check for single adapter + { + Node adapter = selectSingleNode(doc, "/" + ADAPTER_DEFINITION_ELEMENT); + if (adapter != null) + { + adapterDefinition(adapter, serviceSettings); + fileResolver.popIncludedFile(); + } + else + { + // The {0} root element in file {1} must be ''{2}'' or ''{3}''. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_INCLUDE_ROOT, new Object[]{ADAPTER_INCLUDE_ELEMENT, src, ADAPTERS_ELEMENT, ADAPTER_DEFINITION_ELEMENT}); + throw ex; + } + } + } + + private void destinationInclude(Node destInclude, ServiceSettings serviceSettings) + { + // Validation + allowedAttributesOrElements(destInclude, DESTINATION_INCLUDE_CHILDREN); + + String src = getAttributeOrChildElement(destInclude, SRC_ATTR); + String dir = getAttributeOrChildElement(destInclude, DIRECTORY_ATTR); + if (src.length() > 0) + { + destinationIncludeFile(serviceSettings, src); + } + else if (dir.length() > 0) + { + destinationIncludeDirectory(serviceSettings, dir); + } + else + { + // The include element ''{0}'' must specify either the ''{1}'' or ''{2}'' attribute. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(MISSING_INCLUDE_ATTRIBUTES, new Object[]{destInclude.getNodeName(), SRC_ATTR, DIRECTORY_ATTR}); + throw ex; + } + } + + private void destinationIncludeDirectory(ServiceSettings serviceSettings, String dir) + { + List files = fileResolver.getFiles(dir); + for (int i = 0; i < files.size(); i++) + { + String src = (String) files.get(i); + destinationIncludeFile(serviceSettings, src); + } + } + + private void destinationIncludeFile(ServiceSettings serviceSettings, String src) + { + Document doc = loadDocument(src, fileResolver.getIncludedFile(src)); + doc.getDocumentElement().normalize(); + + // Check for multiple destination defined in file. + Node destinationsNode = selectSingleNode(doc, DESTINATIONS_ELEMENT); + if (destinationsNode != null) + { + allowedChildElements(destinationsNode, DESTINATIONS_CHILDREN); + NodeList destinations = selectNodeList(destinationsNode, DESTINATION_ELEMENT); + for (int a = 0; a < destinations.getLength(); a++) + { + Node dest = destinations.item(a); + destination(dest, serviceSettings); + } + fileResolver.popIncludedFile(); + } + else // Check for single destination definition. + { + Node dest = selectSingleNode(doc, "/" + DESTINATION_ELEMENT); + if (dest != null) + { + destination(dest, serviceSettings); + fileResolver.popIncludedFile(); + } + else + { + // The {0} root element in file {1} must be ''{2}'' or ''{3}''. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_INCLUDE_ROOT, new Object[]{DESTINATION_INCLUDE_ELEMENT, src, DESTINATIONS_ELEMENT, DESTINATION_ELEMENT}); + throw ex; + } + } + } + + private void destination(Node dest, ServiceSettings serviceSettings) + { + // Validation + requiredAttributesOrElements(dest, DESTINATION_REQ_CHILDREN); + allowedAttributes(dest, DESTINATION_ATTR); + allowedChildElements(dest, DESTINATION_CHILDREN); + + String serviceId = serviceSettings.getId(); + + DestinationSettings destinationSettings; + String id = getAttributeOrChildElement(dest, ID_ATTR); + if (isValidID(id)) + { + destinationSettings = (DestinationSettings)serviceSettings.getDestinationSettings().get(id); + if (destinationSettings != null) + { + // Duplicate destination definition '{id}' in service '{serviceId}'. + ConfigurationException e = new ConfigurationException(); + e.setMessage(DUPLICATE_DESTINATION_ERROR, new Object[]{id, serviceId}); + throw e; + } + + destinationSettings = new DestinationSettings(id); + destinationSettings.setSourceFile(getSourceFileOf(dest)); + serviceSettings.addDestinationSettings(destinationSettings); + } + else + { + //Invalid {DESTINATION_ELEMENT} id '{id}' for service '{serviceId}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_ID_IN_SERVICE, new Object[]{DESTINATION_ELEMENT, id, serviceId}); + throw ex; + } + + // Destination Properties + NodeList properties = selectNodeList(dest, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(dest)); + destinationSettings.addProperties(map); + + // Sniff for <network><reliable>true|false</reliable></network> setting. + // Also sniff for inbound and outbound throttle policies of buffer and conflate. + // All these features are only supported when advanced messaging support is enabled. + if (!verifyAdvancedMessagingSupport) + { + ConfigMap networkSettings = map.getPropertyAsMap(NetworkSettings.NETWORK_ELEMENT, null); + if (networkSettings != null) + { + String reliable = networkSettings.getPropertyAsString(NetworkSettings.RELIABLE_ELEMENT, null); + if (reliable != null && Boolean.valueOf(reliable)) + { + verifyAdvancedMessagingSupport = true; + } + else + { + ConfigMap inbound = networkSettings.getPropertyAsMap(ThrottleSettings.ELEMENT_INBOUND, null); + if (inbound != null) + { + String policy = inbound.getPropertyAsString(ThrottleSettings.ELEMENT_POLICY, null); + if (policy != null && (Policy.BUFFER.toString().equalsIgnoreCase(policy) + || Policy.CONFLATE.toString().equalsIgnoreCase(policy))) + verifyAdvancedMessagingSupport = true; + } + if (!verifyAdvancedMessagingSupport) + { + ConfigMap outbound = networkSettings.getPropertyAsMap(ThrottleSettings.ELEMENT_OUTBOUND, null); + if (outbound != null) + { + String policy = outbound.getPropertyAsString(ThrottleSettings.ELEMENT_POLICY, null); + if (policy != null && (Policy.BUFFER.toString().equalsIgnoreCase(policy) + || Policy.CONFLATE.toString().equalsIgnoreCase(policy))) + verifyAdvancedMessagingSupport = true; + } + } + } + } + } + } + + // Channels + destinationChannels(dest, destinationSettings, serviceSettings); + + // Security + destinationSecurity(dest, destinationSettings, serviceSettings); + + // Service Adapter + destinationAdapter(dest, destinationSettings, serviceSettings); + } + + private void destinationChannels(Node dest, DestinationSettings destinationSettings, ServiceSettings serviceSettings) + { + String destId = destinationSettings.getId(); + + // Channels attribute + String channelsList = evaluateExpression(dest, "@" + CHANNELS_ATTR).toString().trim(); + if (channelsList.length() > 0) + { + StringTokenizer st = new StringTokenizer(channelsList, LIST_DELIMITERS); + while (st.hasMoreTokens()) + { + String ref = st.nextToken().trim(); + ChannelSettings channel = config.getChannelSettings(ref); + if (channel != null) + { + destinationSettings.addChannelSettings(channel); + } + else + { + // {CHANNEL_ELEMENT} not found for reference '{ref}' in destination '{destId}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(REF_NOT_FOUND_IN_DEST, new Object[]{CHANNEL_ELEMENT, ref, destId}); + throw ex; + } + } + } + else + { + // Channels element + Node channelsNode = selectSingleNode(dest, CHANNELS_ELEMENT); + if (channelsNode != null) + { + allowedChildElements(channelsNode, DESTINATION_CHANNELS_CHILDREN); + NodeList channels = selectNodeList(channelsNode, CHANNEL_ELEMENT); + for (int c = 0; c < channels.getLength(); c++) + { + Node chan = channels.item(c); + + // Validation + requiredAttributesOrElements(chan, DESTINATION_CHANNEL_REQ_CHILDREN); + + String ref = getAttributeOrChildElement(chan, REF_ATTR); + if (ref.length() > 0) + { + ChannelSettings channel = config.getChannelSettings(ref); + if (channel != null) + { + destinationSettings.addChannelSettings(channel); + } + else + { + // {CHANNEL_ELEMENT} not found for reference '{ref}' in destination '{destId}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(REF_NOT_FOUND_IN_DEST, new Object[]{CHANNEL_ELEMENT, ref, destId}); + throw ex; + } + } + else + { + //Invalid {0} ref '{1}' in destination '{2}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_REF_IN_DEST, new Object[]{CHANNEL_ELEMENT, ref, destId}); + throw ex; + } + } + } + else + { + // Finally, we fall back to the service's default channels + List defaultChannels = serviceSettings.getDefaultChannels(); + Iterator it = defaultChannels.iterator(); + while (it.hasNext()) + { + ChannelSettings channel = (ChannelSettings)it.next(); + destinationSettings.addChannelSettings(channel); + } + } + } + + if (destinationSettings.getChannelSettings().size() <= 0) + { + // Destination '{id}' must specify at least one channel. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(DEST_NEEDS_CHANNEL, new Object[]{destId}); + throw ex; + } + } + + private void destinationSecurity(Node dest, DestinationSettings destinationSettings, ServiceSettings serviceSettings) + { + String destId = destinationSettings.getId(); + + // Security-constraint short-cut attribute + String ref = evaluateExpression(dest, "@" + SECURITY_CONSTRAINT_ATTR).toString().trim(); + if (ref.length() > 0) + { + SecurityConstraint sc = ((MessagingConfiguration)config).getSecuritySettings().getConstraint(ref); + if (sc != null) + { + destinationSettings.setConstraint(sc); + } + else + { + // {SECURITY_CONSTRAINT_ELEMENT} not found for reference '{ref}' in destination '{destId}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(REF_NOT_FOUND_IN_DEST, new Object[]{SECURITY_CONSTRAINT_ATTR, ref, destId}); + throw ex; + } + } + else + { + // Inline security element + Node security = selectSingleNode(dest, SECURITY_ELEMENT); + if (security != null) + { + allowedChildElements(security, EMBEDDED_SECURITY_CHILDREN); + Node constraint = selectSingleNode(security, SECURITY_CONSTRAINT_ELEMENT); + if (constraint != null) + { + SecurityConstraint sc = securityConstraint(constraint, true); + destinationSettings.setConstraint(sc); + } + } + else + { + // Finally, we fall back to the service's default security constraint + SecurityConstraint sc = serviceSettings.getConstraint(); + if (sc != null) + { + destinationSettings.setConstraint(sc); + } + } + } + } + + private void destinationAdapter(Node dest, DestinationSettings destinationSettings, ServiceSettings serviceSettings) + { + String destId = destinationSettings.getId(); + + // Adapter attribute + String ref = evaluateExpression(dest, "@" + ADAPTER_ATTR).toString().trim(); + if (ref.length() > 0) + { + adapterReference(ref, destinationSettings, serviceSettings); + } + else + { + Node adapter = selectSingleNode(dest, ADAPTER_ELEMENT); + + // Adapter element + if (adapter != null) + { + allowedAttributesOrElements(adapter, DESTINATION_ADAPTER_CHILDREN); + ref = getAttributeOrChildElement(adapter, REF_ATTR); + adapterReference(ref, destinationSettings, serviceSettings); + } + else + { + // Default Adapter (optionally set at the service level) + AdapterSettings adapterSettings = serviceSettings.getDefaultAdapter(); + if (adapterSettings != null) + { + destinationSettings.setAdapterSettings(adapterSettings); + } + } + } + + if (destinationSettings.getAdapterSettings() == null) + { + // Destination '{id}' must specify at least one adapter. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(DEST_NEEDS_ADAPTER, new Object[]{destId}); + throw ex; + } + } + + private void adapterReference(String ref, DestinationSettings destinationSettings, ServiceSettings serviceSettings) + { + String destId = destinationSettings.getId(); + if (ref.length() > 0) + { + AdapterSettings adapterSettings = serviceSettings.getAdapterSettings(ref); + if (adapterSettings != null) + { + destinationSettings.setAdapterSettings(adapterSettings); + } + else + { + // {ADAPTER_ELEMENT} not found for reference '{ref}' in destination '{destId}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(REF_NOT_FOUND_IN_DEST, new Object[]{ADAPTER_ELEMENT, ref, destId}); + throw ex; + } + } + else + { + //Invalid {ADAPTER_ELEMENT} ref '{ref}' in destination '{destId}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_REF_IN_DEST, new Object[]{ADAPTER_ELEMENT, ref, destId}); + throw ex; + } + } + + private void logging(Node root) + { + Node logging = selectSingleNode(root, LOGGING_ELEMENT); + if (logging != null) + { + // Validation + allowedAttributesOrElements(logging, LOGGING_CHILDREN); + + LoggingSettings settings = new LoggingSettings(); + + // Log Properties + NodeList properties = selectNodeList(logging, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(logging)); + settings.addProperties(map); + } + + NodeList targets = selectNodeList(logging, TARGET_ELEMENT); + for (int i = 0; i < targets.getLength(); i++) + { + Node targetNode = targets.item(i); + + // Target Validation + requiredAttributesOrElements(targetNode, TARGET_REQ_CHILDREN); + allowedAttributesOrElements(targetNode, TARGET_CHILDREN); + + String className = getAttributeOrChildElement(targetNode, CLASS_ATTR); + + if (className.length() > 0) + { + TargetSettings targetSettings = new TargetSettings(className); + String targetLevel = getAttributeOrChildElement(targetNode, LEVEL_ATTR); + + if (targetLevel.length() > 0) + targetSettings.setLevel(targetLevel); + + // Filters + Node filtersNode = selectSingleNode(targetNode, FILTERS_ELEMENT); + if (filtersNode != null) + { + allowedChildElements(filtersNode, FILTERS_CHILDREN); + NodeList filters = selectNodeList(filtersNode, PATTERN_ELEMENT); + for (int f = 0; f < filters.getLength(); f++) + { + Node pattern = filters.item(f); + String filter = evaluateExpression(pattern, ".").toString().trim(); + targetSettings.addFilter(filter); + } + } + + // Target Properties + properties = selectNodeList(targetNode, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(targetNode)); + targetSettings.addProperties(map); + } + + settings.addTarget(targetSettings); + } + } + + config.setLoggingSettings(settings); + } + } + + private void system(Node root) + { + Node system = selectSingleNode(root, SYSTEM_ELEMENT); + if (system == null) + { + // Create a default instance of SystemSettings which by default has setManagable as true + // and has setRedeployEnabled as false. + ((MessagingConfiguration)config).setSystemSettings(new SystemSettings()); + return; + } + + allowedAttributesOrElements(system, SYSTEM_CHILDREN); + + SystemSettings settings = new SystemSettings(); + + settings.setEnforceEndpointValidation(getAttributeOrChildElement(system, ENFORCE_ENDOINT_VALIDATION)); + locale(system, settings); + settings.setManageable(getAttributeOrChildElement(system, MANAGEABLE_ELEMENT)); + settings.setDotNetFrameworkVersion(getAttributeOrChildElement(system, DOTNET_FRAMEWORK_VERSION)); + redeploy(system, settings); + uuidGenerator(system, settings); + + ((MessagingConfiguration)config).setSystemSettings(settings); + } + + private void redeploy(Node system, SystemSettings settings) + { + Node redeployNode = selectSingleNode(system, REDEPLOY_ELEMENT); + if (redeployNode == null) + return; + + allowedAttributesOrElements(redeployNode, REDEPLOY_CHILDREN); + + String enabled = getAttributeOrChildElement(redeployNode, ENABLED_ELEMENT); + settings.setRedeployEnabled(enabled); + + String interval = getAttributeOrChildElement(redeployNode, WATCH_INTERVAL_ELEMENT); + if (interval.length() > 0) + { + settings.setWatchInterval(interval); + } + + NodeList watches = selectNodeList(redeployNode, WATCH_FILE_ELEMENT); + for (int i = 0; i < watches.getLength(); i++) + { + Node watchNode = watches.item(i); + String watch = evaluateExpression(watchNode, ".").toString().trim(); + if (watch.length() > 0) + { + settings.addWatchFile(watch); + } + } + + NodeList touches = selectNodeList(redeployNode, TOUCH_FILE_ELEMENT); + for (int i = 0; i < touches.getLength(); i++) + { + Node touchNode = touches.item(i); + String touch = evaluateExpression(touchNode, ".").toString().trim(); + if (touch.length() > 0) + { + settings.addTouchFile(touch); + } + } + } + + private void locale(Node system, SystemSettings settings) + { + Node localeNode = selectSingleNode(system, LOCALE_ELEMENT); + if (localeNode == null) + return; + + allowedAttributesOrElements(localeNode, LOCALE_CHILDREN); + + String defaultLocaleString = getAttributeOrChildElement(localeNode, DEFAULT_LOCALE_ELEMENT); + Locale defaultLocale = defaultLocaleString.length() > 0? LocaleUtils.buildLocale(defaultLocaleString) : LocaleUtils.buildLocale(null); + settings.setDefaultLocale(defaultLocale); + } + + private void uuidGenerator(Node system, SystemSettings settings) + { + Node uuidGenerator = selectSingleNode(system, UUID_GENERATOR_ELEMENT); + if (uuidGenerator == null) + return; + + requiredAttributesOrElements(uuidGenerator, UUID_GENERATOR_REQ_CHILDREN); + + String className = getAttributeOrChildElement(uuidGenerator, CLASS_ATTR); + if (className.length() == 0) + { + // Class not specified for {UUID_GENERATOR_ELEMENT} '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(CLASS_NOT_SPECIFIED, new Object[]{UUID_GENERATOR_ELEMENT, ""}); + throw ex; + } + + settings.setUUIDGeneratorClassName(className); + } + + private void flexClient(Node root) + { + Node flexClient = selectSingleNode(root, FLEX_CLIENT_ELEMENT); + if (flexClient != null) + { + // Validation + allowedChildElements(flexClient, FLEX_CLIENT_CHILDREN); + + FlexClientSettings flexClientSettings = new FlexClientSettings(); + + // Timeout + String timeout = getAttributeOrChildElement(flexClient, FLEX_CLIENT_TIMEOUT_MINUTES_ELEMENT); + if (timeout.length() > 0) + { + try + { + long timeoutMinutes = Long.parseLong(timeout); + if (timeoutMinutes < 0) + { + // Invalid timeout minutes value ''{0}'' in the <flex-client> configuration section. Please specify a positive value or leave the element undefined in which case flex client instances on the server will be timed out when all associated sessions/connections have shut down. + ConfigurationException e = new ConfigurationException(); + e.setMessage(INVALID_FLEX_CLIENT_TIMEOUT, new Object[]{timeout}); + throw e; + } + flexClientSettings.setTimeoutMinutes(timeoutMinutes); + } + catch (NumberFormatException nfe) + { + // Invalid timeout minutes value ''{0}'' in the <flex-client> configuration section. Please specify a positive value or leave the element undefined in which case flex client instances on the server will be timed out when all associated sessions/connections have shut down. + ConfigurationException e = new ConfigurationException(); + e.setMessage(INVALID_FLEX_CLIENT_TIMEOUT, new Object[]{timeout}); + throw e; + } + } + else + { + flexClientSettings.setTimeoutMinutes(0); // Default to 0; in this case FlexClients are invalidated when all associated sessions have been invalidated. + } + + // Flex client queue processor + Node outboundQueueProcessor = selectSingleNode(flexClient, FLEX_CLIENT_OUTBOUND_QUEUE_PROCESSOR_ELEMENT); + if (outboundQueueProcessor != null) + { + // Validation + requiredAttributesOrElements(outboundQueueProcessor, FLEX_CLIENT_OUTBOUND_QUEUE_PROCESSOR_REQ_CHILDREN); + + // Flex client queue processor class + String outboundQueueProcessClass = getAttributeOrChildElement(outboundQueueProcessor, CLASS_ATTR); + if (outboundQueueProcessClass.length() > 0) + { + flexClientSettings.setFlexClientOutboundQueueProcessorClassName(outboundQueueProcessClass); + } + else + { + // Class not specified for {FLEX_CLIENT_OUTBOUND_QUEUE_PROCESSOR_ELEMENT} '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(CLASS_NOT_SPECIFIED, new Object[]{FLEX_CLIENT_OUTBOUND_QUEUE_PROCESSOR_ELEMENT, ""}); + throw ex; + } + + // Flex client queue processor properties + NodeList properties = selectNodeList(outboundQueueProcessor, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(outboundQueueProcessor)); + flexClientSettings.setFlexClientOutboundQueueProcessorProperties(map); + // Sniff for adaptive-frequency which requires advanced messaging support. + boolean adaptiveFrequency = map.getPropertyAsBoolean(ADAPTIVE_FREQUENCY, false); + if (adaptiveFrequency) + verifyAdvancedMessagingSupport = true; + } + } + ((MessagingConfiguration)config).setFlexClientSettings(flexClientSettings); + } + } + + private void factories(Node root) + { + Node factories = selectSingleNode(root, FACTORIES_ELEMENT); + if (factories != null) + { + // Validation + allowedAttributesOrElements(factories, FACTORIES_CHILDREN); + + NodeList factoryList = selectNodeList(factories, FACTORY_ELEMENT); + for (int i = 0; i < factoryList.getLength(); i++) + { + Node factory = factoryList.item(i); + factory(factory); + } + } + } + + private void factory(Node factory) + { + // Validation + requiredAttributesOrElements(factory, FACTORY_REQ_CHILDREN); + + String id = getAttributeOrChildElement(factory, ID_ATTR); + String className = getAttributeOrChildElement(factory, CLASS_ATTR); + if (isValidID(id)) + { + FactorySettings factorySettings = new FactorySettings(id, className); + + // Factory Properties + NodeList properties = selectNodeList(factory, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(factory)); + factorySettings.addProperties(map); + } + ((MessagingConfiguration)config).addFactorySettings(id, factorySettings); + } + else + { + // Invalid {FACTORY_ELEMENT} id '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(INVALID_ID, new Object[]{FACTORY_ELEMENT, id}); + ex.setDetails(INVALID_ID); + throw ex; + } + } + + private void messageFilters(Node root) + { + typedMessageFilters(root, ASYNC_MESSAGE_FILTERS_ELEMENT, ASYNC_MESSAGE_FILTERS_ELEMENT_CHILDREN); + typedMessageFilters(root, SYNC_MESSAGE_FILTERS_ELEMENT, SYNC_MESSAGE_FILTERS_ELEMENT_CHILDREN); + } + + private void typedMessageFilters(Node root, String filterTypeElement, String[] childrenElements) + { + Node messageFiltersNode = selectSingleNode(root, filterTypeElement); + if (messageFiltersNode == null) + return; + + // Validation + allowedChildElements(messageFiltersNode, childrenElements); + + // Message filter + NodeList messageFilters = selectNodeList(messageFiltersNode, FILTER_ELEMENT); + for (int i = 0; i < messageFilters.getLength(); i++) + { + Node messageFilter = messageFilters.item(i); + messageFilter(messageFilter, filterTypeElement); + } + } + + private void messageFilter(Node messageFilter, String filterType) + { + // Validation + requiredAttributesOrElements(messageFilter, FILTER_REQ_CHILDREN); + allowedAttributesOrElements(messageFilter, FILTER_CHILDREN); + + String id = getAttributeOrChildElement(messageFilter, ID_ATTR); + if (isValidID(id)) + { + // Message filter class name + String className = getAttributeOrChildElement(messageFilter, CLASS_ATTR); + if (className.length() > 0) + { + MessageFilterSettings messageFilterSettings = new MessageFilterSettings(); + messageFilterSettings.setId(id); + messageFilterSettings.setClassName(className); + // Record type of filter. + MessageFilterSettings.FilterType type = filterType.equals(ASYNC_MESSAGE_FILTERS_ELEMENT) ? + MessageFilterSettings.FilterType.ASYNC : + MessageFilterSettings.FilterType.SYNC; + messageFilterSettings.setFilterType(type); + // Custom server properties. + NodeList properties = selectNodeList(messageFilter, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(messageFilter)); + messageFilterSettings.addProperties(map); + } + ((MessagingConfiguration)config).addMessageFilterSettings(messageFilterSettings); + } + else + { + // Class not specified for {FILTER_ELEMENT} '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(CLASS_NOT_SPECIFIED, new Object[]{FILTER_ELEMENT, id}); + throw ex; + } + } + } + + private void validators(Node root) + { + Node validatorsNode = selectSingleNode(root, VALIDATORS_ELEMENT); + if (validatorsNode == null) + return; + + // Validation + allowedChildElements(validatorsNode, VALIDATORS_CHILDREN); + + // Validator + NodeList validators = selectNodeList(validatorsNode, VALIDATOR_ELEMENT); + for (int i = 0; i < validators.getLength(); i++) + { + Node validator = validators.item(i); + validator(validator); + } + } + + private void validator(Node validator) + { + // Validation + requiredAttributesOrElements(validator, VALIDATOR_REQ_CHILDREN); + allowedAttributesOrElements(validator, VALIDATOR_CHILDREN); + + ValidatorSettings validatorSettings = new ValidatorSettings(); + + // Validator class name + String className = getAttributeOrChildElement(validator, CLASS_ATTR); + if (className.length() > 0) + { + validatorSettings.setClassName(className); + } + else + { + // Class not specified for {VALIDATOR_ELEMENT} '{id}'. + ConfigurationException ex = new ConfigurationException(); + ex.setMessage(CLASS_NOT_SPECIFIED, new Object[]{VALIDATOR_ELEMENT, ""}); + throw ex; + } + + // Validator type + String type = getAttributeOrChildElement(validator, TYPE_ATTR); + if (type.length() > 0) + validatorSettings.setType(type); + + // Validator properties + NodeList properties = selectNodeList(validator, PROPERTIES_ELEMENT + "/*"); + if (properties.getLength() > 0) + { + ConfigMap map = properties(properties, getSourceFileOf(validator)); + validatorSettings.addProperties(map); + } + + ((MessagingConfiguration)config).addValidatorSettings(validatorSettings); + } +}
http://git-wip-us.apache.org/repos/asf/flex-blazeds/blob/bf2e1dc9/core/src/flex/messaging/config/ServerSettings.java ---------------------------------------------------------------------- diff --git a/core/src/flex/messaging/config/ServerSettings.java b/core/src/flex/messaging/config/ServerSettings.java new file mode 100644 index 0000000..77f61e9 --- /dev/null +++ b/core/src/flex/messaging/config/ServerSettings.java @@ -0,0 +1,231 @@ +/* + * 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.services.messaging.MessagingConstants; + +/** + * Server settings for a message destination. + */ +public class ServerSettings +{ + // Errors + private static final int INVALID_CLUSTER_MESSAGE_ROUTING = 11121; + + private boolean allowSubtopics; + private boolean disallowWildcardSubtopics; + private long messageTTL = -1; //We need to keep track of uninitialized value + private boolean isDurable; + private int priority = -1; + private String subtopicSeparator; + private RoutingMode routingMode = RoutingMode.SERVER_TO_SERVER; + + public static enum RoutingMode {NONE,BROADCAST,SERVER_TO_SERVER}; + + /** + * Creates a default <code>ServerSettings</code> instance. + */ + public ServerSettings() + { + isDurable = false; + subtopicSeparator = MessagingConstants.DEFAULT_SUBTOPIC_SEPARATOR; + } + + /** + * Returns <code>allow-subtopics</code> property. + * + * @return a boolean specifying whether or not the server has the <code>allow-subtopics</code> property. + */ + public boolean getAllowSubtopics() + { + return allowSubtopics; + } + + /** + * Sets <code>allow-subtopics</code> property. + * + * @param value The value for <code>allow-subtopics</code> property. + */ + public void setAllowSubtopics(boolean value) + { + allowSubtopics = value; + } + + /** + * Returns <code>cluster-message-routing</code> property. + * + * @return a boolean specifying whether or not the server has the <code>cluster-message-routing</code> property. + */ + public boolean isBroadcastRoutingMode() + { + return routingMode == RoutingMode.BROADCAST; + } + + /** + * Returns the routing mode configured. + * + * @return The configured routing mode. + */ + public RoutingMode getRoutingMode() + { + return routingMode; + } + + /** + * Sets the <code>cluster-message-routing</code> property. + * + * @param routingMode <code>server-to-server</code>(default) + * or <code>broadcast</code>. + */ + public void setBroadcastRoutingMode(String routingMode) + { + if (routingMode.equalsIgnoreCase("broadcast")) + this.routingMode = RoutingMode.BROADCAST; + else if (routingMode.equalsIgnoreCase("server-to-server")) + this.routingMode = RoutingMode.SERVER_TO_SERVER; + else if (routingMode.equalsIgnoreCase("none")) + this.routingMode = RoutingMode.NONE; + else + { + ConfigurationException ce = new ConfigurationException(); + ce.setMessage(INVALID_CLUSTER_MESSAGE_ROUTING, new Object[] {routingMode}); + throw ce; + } + } + + /** + * @deprecated Not used anymore. + */ + public int getMaxCacheSize() + { + return 0; + } + + /** + * @deprecated Not used anymore. + */ + public void setMaxCacheSize(int size) + { + // No-op. + } + + /** + * Returns the <code>message-time-to-live</code> property. + * + * @return the <code>message-time-to-live</code> property. + */ + public long getMessageTTL() + { + return messageTTL; + } + + /** + * Sets the <code>message-time-to-live</code> property. Default value is -1. + * + * @param ttl The value for <code>message-time-to-live</code> property. + */ + public void setMessageTTL(long ttl) + { + messageTTL = ttl; + } + + /** + * Returns true if wildcard subtopics are disallowed. + * + * @return true if wilcard subtopics are disallowed. + */ + public boolean isDisallowWildcardSubtopics() + { + return disallowWildcardSubtopics; + } + + /** + * Sets whether the wildcard subtopics are disallowed. + * + * @param value Whether the wildcard subtopics are disallowed. + */ + public void setDisallowWildcardSubtopics(boolean value) + { + disallowWildcardSubtopics = value; + } + + /** + * Returns whether destination is durable. + * + * @return <code>true</code> if destination is durable; otherwise <code>false</code>. + */ + public boolean isDurable() + { + return isDurable; + } + + /** + * Sets whether destination is durable. Default value is <code>false</code>. + * + * @param durable The value for <code>durable</code> property. + */ + public void setDurable(boolean durable) + { + isDurable = durable; + } + + /** + * Returns the default message priority for the destination which is a numerical + * value between 0 and 9 (and -1 means no default priority). + * + * @return The default message priority for the destination. + */ + public int getPriority() + { + return priority; + } + + /** + * Sets the default message priority of the destination which should be + * a numerical value between 0 and 9. Values less than 0 and greater than 9 + * are treated as 0 and 9 respectively. + * + * @param priority The new value for the priority. + */ + public void setPriority(int priority) + { + priority = priority < 0? 0 : priority > 9? 9 : priority; + this.priority = priority; + } + + + /** + * Returns the <code>subtopic-separator</code> property. + * + * @return the <code>subtopic-separator</code> property. + */ + public String getSubtopicSeparator() + { + return subtopicSeparator; + } + + /** + * Sets the <code>subtopic-separator</code> property. + * Optional; the default value is period. + * + * @param value The value for <code>subtopic-separator</code> property. + */ + public void setSubtopicSeparator(String value) + { + subtopicSeparator = value; + } +}
