| /* |
| * 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.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| /** |
| * A special mxmlc compiler specific implentation of the configuration |
| * parser for JDK 1.4. Only a small subset of the configuration is |
| * processed to generate the information that the client needs at runtime, |
| * such as channel definitions and service destination properties. |
| */ |
| public abstract class ClientConfigurationParser extends AbstractConfigurationParser { |
| protected void parseTopLevelConfig(Document doc) { |
| Node root = selectSingleNode(doc, "/" + SERVICES_CONFIG_ELEMENT); |
| |
| if (root != null) { |
| // Validation |
| allowedChildElements(root, SERVICES_CONFIG_CHILDREN); |
| |
| // Channels (parse before services) |
| channelsSection(root); |
| |
| // Services |
| services(root); |
| |
| // Clustering |
| clusters(root); |
| |
| // FlexClient |
| flexClient(root); |
| } |
| } |
| |
| 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).trim(); |
| 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); |
| |
| // Endpoint |
| Node endpoint = selectSingleNode(channel, ENDPOINT_ELEMENT); |
| if (endpoint != null) { |
| // Endpoint Validation |
| allowedAttributesOrElements(endpoint, ENDPOINT_CHILDREN); |
| |
| // 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); |
| |
| // Add the channel properties that the client needs namely polling-enabled, |
| // polling-interval-millis, piggybacking-enabled, login-after-disconnect, |
| // record-message-sizes, record-message-times, connect-timeout-seconds, |
| // polling-interval-seconds (deprecated), and client-load-balancing. |
| addProperty(channel, channelSettings, POLLING_ENABLED_ELEMENT); |
| addProperty(channel, channelSettings, POLLING_INTERVAL_MILLIS_ELEMENT); |
| addProperty(channel, channelSettings, PIGGYBACKING_ENABLED_ELEMENT); |
| addProperty(channel, channelSettings, LOGIN_AFTER_DISCONNECT_ELEMENT); |
| addProperty(channel, channelSettings, RECORD_MESSAGE_SIZES_ELEMENT); |
| addProperty(channel, channelSettings, RECORD_MESSAGE_TIMES_ELEMENT); |
| addProperty(channel, channelSettings, CONNECT_TIMEOUT_SECONDS_ELEMENT); |
| addProperty(channel, channelSettings, POLLING_INTERVAL_SECONDS_ELEMENT); // deprecated. |
| addProperty(channel, channelSettings, CLIENT_LOAD_BALANCING_ELEMENT); |
| addProperty(channel, channelSettings, REQUEST_TIMEOUT_SECONDS_ELEMENT); |
| |
| // enable-small-messages. |
| NodeList properties = selectNodeList(channel, PROPERTIES_ELEMENT + "/" + SERIALIZATION_ELEMENT); |
| if (properties.getLength() > 0) { |
| ConfigMap map = properties(properties, getSourceFileOf(channel)); |
| ConfigMap serialization = map.getPropertyAsMap(SERIALIZATION_ELEMENT, null); |
| if (serialization != null) { |
| // enable-small-messages. |
| String enableSmallMessages = serialization.getProperty(ENABLE_SMALL_MESSAGES_ELEMENT); |
| if (enableSmallMessages != null) { |
| ConfigMap clientMap = new ConfigMap(); |
| clientMap.addProperty(ENABLE_SMALL_MESSAGES_ELEMENT, enableSmallMessages); |
| channelSettings.addProperty(SERIALIZATION_ELEMENT, clientMap); |
| } |
| } |
| } |
| } else { |
| // Invalid {CHANNEL_DEFINITION_ELEMENT} id '{id}'. |
| ConfigurationException ex = new ConfigurationException(); |
| ex.setMessage(INVALID_ID, new Object[]{CHANNEL_DEFINITION_ELEMENT, id}); |
| String details = "An id must be non-empty and not contain any list delimiter characters, i.e. commas, semi-colons or colons."; |
| ex.setDetails(details); |
| 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).booleanValue(); |
| |
| 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 addProperty(Node channel, ChannelSettings channelSettings, String property) { |
| NodeList properties = selectNodeList(channel, PROPERTIES_ELEMENT + "/" + property); |
| if (properties.getLength() > 0) { |
| ConfigMap map = properties(properties, getSourceFileOf(channel)); |
| if (CLIENT_LOAD_BALANCING_ELEMENT.equals(property)) { |
| ConfigMap clientLoadBalancingMap = map.getPropertyAsMap(CLIENT_LOAD_BALANCING_ELEMENT, null); |
| if (clientLoadBalancingMap == null) { |
| // Invalid {0} configuration for endpoint ''{1}''; no urls defined. |
| ConfigurationException ce = new ConfigurationException(); |
| ce.setMessage(ERR_MSG_EMPTY_CLIENT_LOAD_BALANCING_ELEMENT, new Object[]{CLIENT_LOAD_BALANCING_ELEMENT, channelSettings.getId()}); |
| throw ce; |
| } |
| List urls = clientLoadBalancingMap.getPropertyAsList(URL_ATTR, null); |
| addClientLoadBalancingUrls(urls, channelSettings.getId()); |
| } |
| channelSettings.addProperties(map); |
| } |
| } |
| |
| // Add client load balancing urls after necessary validation checks. |
| private void addClientLoadBalancingUrls(List urls, String endpointId) { |
| if (urls == null || urls.isEmpty()) { |
| // Invalid {0} configuration for endpoint ''{1}''; no urls defined. |
| ConfigurationException ce = new ConfigurationException(); |
| ce.setMessage(ERR_MSG_EMPTY_CLIENT_LOAD_BALANCING_ELEMENT, new Object[]{CLIENT_LOAD_BALANCING_ELEMENT, endpointId}); |
| throw ce; |
| } |
| |
| Set clientLoadBalancingUrls = new HashSet(); |
| for (Iterator iterator = urls.iterator(); iterator.hasNext(); ) { |
| String url = (String) iterator.next(); |
| if (url == null || url.length() == 0) { |
| // Invalid {0} configuration for endpoint ''{1}''; cannot add empty url. |
| ConfigurationException ce = new ConfigurationException(); |
| ce.setMessage(ERR_MSG_EMTPY_CLIENT_LOAD_BALACNING_URL, new Object[]{CLIENT_LOAD_BALANCING_ELEMENT, endpointId}); |
| throw ce; |
| } |
| |
| if (TokenReplacer.containsTokens(url)) { |
| // Invalid {0} configuration for endpoint ''{1}''; cannot add url with tokens. |
| ConfigurationException ce = new ConfigurationException(); |
| ce.setMessage(ERR_MSG_CLIENT_LOAD_BALANCING_URL_WITH_TOKEN, new Object[]{CLIENT_LOAD_BALANCING_ELEMENT, endpointId}); |
| throw ce; |
| } |
| |
| if (clientLoadBalancingUrls.contains(url)) |
| iterator.remove(); |
| else |
| clientLoadBalancingUrls.add(url); |
| } |
| |
| } |
| |
| 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)); |
| if (fileResolver instanceof LocalFileResolver) { |
| LocalFileResolver local = (LocalFileResolver) fileResolver; |
| ((ClientConfiguration) config).addConfigPath(local.getIncludedPath(src), local.getIncludedLastModified(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 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)) { |
| String propsFileName = getAttributeOrChildElement(cluster, CLUSTER_PROPERTIES_ATTR); |
| ClusterSettings clusterSettings = new ClusterSettings(); |
| clusterSettings.setClusterName(clusterName); |
| clusterSettings.setPropsFileName(propsFileName); |
| String defaultValue = getAttributeOrChildElement(cluster, ClusterSettings.DEFAULT_ELEMENT); |
| if (defaultValue != null && defaultValue.length() > 0) { |
| if (defaultValue.equalsIgnoreCase("true")) |
| clusterSettings.setDefault(true); |
| else if (!defaultValue.equalsIgnoreCase("false")) { |
| 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")) |
| clusterSettings.setURLLoadBalancing(false); |
| else if (!ulb.equalsIgnoreCase("true")) { |
| ConfigurationException e = new ConfigurationException(); |
| e.setMessage(10216, new Object[]{clusterName, ulb}); |
| throw e; |
| } |
| } |
| ((ClientConfiguration) config).addClusterSettings(clusterSettings); |
| } |
| } |
| } |
| } |
| |
| 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)); |
| if (fileResolver instanceof LocalFileResolver) { |
| LocalFileResolver local = (LocalFileResolver) fileResolver; |
| ((ClientConfiguration) config).addConfigPath(local.getIncludedPath(src), local.getIncludedLastModified(src)); |
| } |
| |
| doc.getDocumentElement().normalize(); |
| |
| // Check for multiple services defined in file. |
| Node servicesNode = selectSingleNode(doc, SERVICES_ELEMENT); |
| if (servicesNode != null) { |
| allowedChildElements(servicesNode, SERVICES_CHILDREN); |
| NodeList services = selectNodeList(servicesNode, SERVICES_ELEMENT); |
| for (int a = 0; a < services.getLength(); a++) { |
| Node service = services.item(a); |
| service(service); |
| } |
| fileResolver.popIncludedFile(); |
| } else // Check for single service in file. |
| { |
| 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); |
| // Service Properties |
| NodeList properties = selectNodeList(service, PROPERTIES_ELEMENT + "/*"); |
| if (properties.getLength() > 0) { |
| ConfigMap map = properties(properties, getSourceFileOf(service)); |
| serviceSettings.addProperties(map); |
| } |
| 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); |
| } 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 |
| |
| // 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); |
| } |
| } |
| |
| // 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; |
| } |
| } |
| |
| /** |
| * 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).trim(); |
| |
| 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 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)); |
| if (fileResolver instanceof LocalFileResolver) { |
| LocalFileResolver local = (LocalFileResolver) fileResolver; |
| ((ClientConfiguration) config).addConfigPath(local.getIncludedPath(src), local.getIncludedLastModified(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); |
| 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); |
| } |
| |
| // Channels |
| destinationChannels(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); |
| if (channels.getLength() > 0) { |
| 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).trim(); |
| 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 flexClient(Node root) { |
| Node flexClient = selectSingleNode(root, FLEX_CLIENT_ELEMENT); |
| if (flexClient != null) { |
| FlexClientSettings flexClientSettings = new FlexClientSettings(); |
| // Reliable reconnect duration millis |
| String reliableReconnectDurationMillis = getAttributeOrChildElement(flexClient, FLEX_CLIENT_RELIABLE_RECONNECT_DURATION_MILLIS); |
| if (reliableReconnectDurationMillis.length() > 0) { |
| try { |
| int millis = Integer.parseInt(reliableReconnectDurationMillis); |
| if (millis < 0) { |
| ConfigurationException e = new ConfigurationException(); |
| e.setMessage(INVALID_FLEX_CLIENT_RELIABLE_RECONNECT_DURATION_MILLIS, new Object[]{reliableReconnectDurationMillis}); |
| throw e; |
| } |
| flexClientSettings.setReliableReconnectDurationMillis(millis); |
| } catch (NumberFormatException nfe) { |
| ConfigurationException e = new ConfigurationException(); |
| e.setMessage(INVALID_FLEX_CLIENT_RELIABLE_RECONNECT_DURATION_MILLIS, new Object[]{reliableReconnectDurationMillis}); |
| throw e; |
| } |
| } else { |
| flexClientSettings.setReliableReconnectDurationMillis(0); // Default is 0. |
| } |
| // heartbeat interval millis |
| String heartbeatIntervalMillis = getAttributeOrChildElement(flexClient, FLEX_CLIENT_HEARTBEAT_INTERVAL_MILLIS); |
| if (heartbeatIntervalMillis.length() > 0) { |
| try { |
| int millis = Integer.parseInt(heartbeatIntervalMillis); |
| if (millis < 0) { |
| ConfigurationException e = new ConfigurationException(); |
| e.setMessage(INVALID_FLEX_CLIENT_HEARTBEAT_INTERVAL_MILLIS, new Object[]{heartbeatIntervalMillis}); |
| throw e; |
| } |
| flexClientSettings.setHeartbeatIntervalMillis(millis); |
| } catch (NumberFormatException nfe) { |
| ConfigurationException e = new ConfigurationException(); |
| e.setMessage(INVALID_FLEX_CLIENT_HEARTBEAT_INTERVAL_MILLIS, new Object[]{heartbeatIntervalMillis}); |
| throw e; |
| } |
| } |
| ((ClientConfiguration) config).setFlexClientSettings(flexClientSettings); |
| } |
| } |
| } |