Package org.apache.cocoon.core.container.spring.avalon

Source Code of org.apache.cocoon.core.container.spring.avalon.ConfigurationReader

/*
* 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 org.apache.cocoon.core.container.spring.avalon;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.cocoon.Constants;
import org.apache.cocoon.spring.configurator.ResourceUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.context.support.ServletContextResourcePatternResolver;
import org.xml.sax.InputSource;

/**
* This component reads in Avalon style configuration files and returns all
* contained components and their configurations.
*
* @since 2.2
* @version $Id: ConfigurationReader.java 596516 2007-11-20 02:39:04Z vgritsenko $
*/
public class ConfigurationReader {

    /** Logger (we use the same logging mechanism as Spring!) */
    protected final Log logger = LogFactory.getLog(getClass());

    /** Resolver for reading configuration files. */
    protected final ServletContextResourcePatternResolver resolver;

    /** The configuration info. */
    protected final ConfigurationInfo configInfo;

    /** All component configurations. */
    protected final List componentConfigs = new ArrayList();

    /** Is this the root context? */
    protected final boolean isRootContext;


    /**
     * This method reads in an Avalon style configuration.
     *
     * @param source         The location of the configuration.
     * @param resourceLoader The resource loader to load included configs.
     * @return               A configuration containing all defined objects.
     * @throws Exception
     */
    public static ConfigurationInfo readConfiguration(String         source,
                                                      ResourceLoader resourceLoader)
    throws Exception {
        final ConfigurationReader converter = new ConfigurationReader(null, resourceLoader);
        converter.convert(source);
        return converter.configInfo;
    }

    /**
     * This method reads in an Avalon style sitemap.
     *
     * @param src         The location of the sitemap.
     * @param resourceLoader The resource loader to load included configs.
     * @return               A configuration containing all defined objects.
     * @throws Exception
     */
    public static ConfigurationInfo readSitemap(ConfigurationInfo parentInfo,
                                                String            src,
                                                ResourceLoader    resourceLoader)
    throws Exception {
        String source = src;
        if (source == null || source.trim().length() == 0) {
            source = "sitemap.xmap";
        }

        final ConfigurationReader converter = new ConfigurationReader(parentInfo, resourceLoader);
        converter.convertSitemap(source);
        return converter.configInfo;
    }

    public static ConfigurationInfo readConfiguration(Configuration     rolesConfig,
                                                      Configuration     componentConfig)
    throws Exception {
        final ConfigurationReader converter = new ConfigurationReader(null, new DefaultResourceLoader());
        converter.convert(rolesConfig, componentConfig, null);
        return converter.configInfo;
    }

    private ConfigurationReader(ConfigurationInfo parentInfo,
                                ResourceLoader    resourceLoader)
    throws Exception {
        if (resourceLoader == null) {
            throw new IllegalArgumentException("ResourceLoader not set!");
        }
        this.isRootContext = parentInfo == null;
        this.resolver = new ServletContextResourcePatternResolver(resourceLoader);

        // now add selectors from parent
        if (parentInfo != null) {
            this.configInfo = new ConfigurationInfo(parentInfo);

            final Iterator i = parentInfo.getComponents().values().iterator();
            while (i.hasNext()) {
                final ComponentInfo current = (ComponentInfo) i.next();
                if (current.isSelector()) {
                    this.configInfo.addRole(current.getRole(), current.copy());
                }
            }

            /* TODO - we should add the processor to each container
                      This would avoid the hacky getting of the current container in the tree processor
            ComponentInfo processorInfo = (ComponentInfo) parentInfo.getComponents().get(Processor.ROLE);
            if (processorInfo != null) {
                this.configInfo.getComponents().put(Processor.ROLE, processorInfo.copy());
            }
            */
        } else {
            this.configInfo = new ConfigurationInfo();
        }
    }

    /**
     * Convert an avalon url (with possible cocoon protocols) to a spring url.
     *
     * @param url The avalon url.
     * @return The spring url.
     */
    protected String convertUrl(String url) {
        if (url == null) {
            return null;
        }
        if (url.startsWith("context:")) {
            return url.substring(10);
        }
        if (url.startsWith("resource:")) {
            return "classpath:" + url.substring(10);
        }
        //if (url.indexOf(':') == -1 && !url.startsWith("/")) {
        //    return '/' + url;
        //}
        return url;
    }

    /**
     * Copied from {@link ResourceUtils#correctUri(String)}. Comment says:
     * <br><blockquote>
     * If it is a file we have to recreate the url, otherwise we get problems
     * under windows with some file references starting with "/DRIVELETTER" and
     * some just with "DRIVELETTER".
     * </blockquote>
     * @param url to correct
     * @return corrected (or same) url
     */
    protected String correctUrl(String url) {
        if (url.startsWith("file:")) {
            final File f = new File(url.substring(5));
            return "file://" + f.getAbsolutePath();
        }

        return url;
    }

    protected String getUrl(Resource rsrc)
    throws IOException {
        if (rsrc instanceof SourceResource) {
            return ((SourceResource) rsrc).getUrlString();
        } else {
            return rsrc.getURL().toExternalForm();
        }
    }

    protected String getUrl(String url, String base) {
        if (url == null || base == null) {
            return convertUrl(url);
        }

        if (url.indexOf(":/") < 2) {
            int posSeparator = base.lastIndexOf('/');
            int posFileSeparator = base.lastIndexOf(File.separatorChar);
            if (posFileSeparator > posSeparator) {
                posSeparator = posFileSeparator;
            }
            return convertUrl(base.substring(0, posSeparator + 1) + url);
        }

        return convertUrl(url);
    }

    /**
     * Construct input source from given Resource, initialize system Id.
     *
     * @param rsrc Resource for the input source
     * @return Input source
     * @throws Exception if resource URL is not valid or input stream is not available
     */
    protected InputSource getInputSource(Resource rsrc)
    throws IOException {
        final InputSource is = new InputSource(rsrc.getInputStream());
        is.setSystemId(getUrl(rsrc));
        return is;
    }

    protected void convert(String relativePath)
    throws Exception {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Reading Avalon configuration from " + relativePath);
        }
        Resource root = this.resolver.getResource(getUrl(relativePath, null));
        final DefaultConfigurationBuilder b = new DefaultConfigurationBuilder(true);

        final Configuration config = b.build(this.getInputSource(root));
        // validate cocoon.xconf
        if (!"cocoon".equals(config.getName())) {
            throw new ConfigurationException("Invalid configuration file\n" +
                                             config);
        }

        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Configuration version: " + config.getAttribute("version"));
        }
        if (!Constants.CONF_VERSION.equals(config.getAttribute("version"))) {
            throw new ConfigurationException("Invalid configuration schema version. Must be '" +
                                             Constants.CONF_VERSION + "'.");
        }

        convert(config, null, getUrl(root));
    }

    protected void convertSitemap(String sitemapLocation)
    throws Exception {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Reading sitemap from " + sitemapLocation);
        }
        final Resource root = this.resolver.getResource(getUrl(sitemapLocation, null));
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Resolved sitemap: " + root.getURL());
        }
        final DefaultConfigurationBuilder b = new DefaultConfigurationBuilder(true);

        final Configuration config = b.build(getInputSource(root));
        // validate sitemap.xmap
        if (!"sitemap".equals(config.getName())) {
            throw new ConfigurationException("Invalid sitemap\n" +
                                             config);
        }

        final Configuration completeConfig = SitemapHelper.createSitemapConfiguration(config);
        if (completeConfig != null) {
            convert(completeConfig, null, getUrl(root));
        }
    }

    protected void convert(Configuration config, Configuration additionalConfig, String rootUri)
    throws Exception {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Converting Avalon configuration from configuration object: " + config);
        }

        // It's possible to define a logger on a per sitemap/service manager base.
        // This is the default logger for all components defined with this sitemap/manager.
        this.configInfo.setRootLogger(config.getAttribute("logger", null));

        // and load configuration with a empty list of loaded configurations
        final Set loadedConfigs = new HashSet();
        // what is it?
        if ("role-list".equals(config.getName()) || "roles".equals(config.getName())) {
            configureRoles(config);
        } else {
            parseConfiguration(config, null, loadedConfigs);
        }

        // test for optional user-roles attribute
        if (rootUri != null) {
            final String userRoles = config.getAttribute("user-roles", null);
            if (userRoles != null) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Reading additional user roles: " + userRoles);
                }
                final Resource userRolesSource = this.resolver.getResource(this.getUrl(userRoles, rootUri));
                final DefaultConfigurationBuilder b = new DefaultConfigurationBuilder(true);
                final Configuration userRolesConfig = b.build(this.getInputSource(userRolesSource));
                parseConfiguration(userRolesConfig, getUrl(userRolesSource), loadedConfigs);
            }
        }
        if (additionalConfig != null) {
            if ("role-list".equals(additionalConfig.getName()) || "roles".equals(additionalConfig.getName())) {
                configureRoles(additionalConfig);
            } else {
                parseConfiguration(additionalConfig, null, loadedConfigs);
            }
        }

        // now process all component configurations
        processComponents();

        // add roles as components
        final Iterator i = this.configInfo.getRoles().iterator();
        while (i.hasNext()) {
            final ComponentInfo current = (ComponentInfo) i.next();
            if (!current.hasConfiguredLazyInit()) {
                current.setLazyInit(true);
            }
            this.configInfo.addComponent(current);
        }
        this.configInfo.clearRoles();
    }

    protected void parseConfiguration(final Configuration configuration,
                                      String              contextURI,
                                      Set                 loadedURIs)
    throws ConfigurationException {
        final Configuration[] configurations = configuration.getChildren();

        for( int i = 0; i < configurations.length; i++ ) {
            final Configuration componentConfig = configurations[i];

            final String componentName = componentConfig.getName();

            if ("include".equals(componentName)) {
                handleInclude(contextURI, loadedURIs, componentConfig);
            } else if ("include-beans".equals(componentName)) {
                // we ignore include-beans if this is a child context as this has already been
                // processed by the sitemap element
                if (this.isRootContext) {
                    handleBeanInclude(contextURI, componentConfig);
                }
                // we ignore include-properties if this is a child context
            } else if (this.isRootContext || !"include-properties".equals(componentName)) {
                // Component declaration, add it to list
                componentConfigs.add(componentConfig);
            }
        }
    }

    protected void processComponents()
    throws ConfigurationException {
        final Iterator i = this.componentConfigs.iterator();
        while (i.hasNext()) {
            final Configuration componentConfig = (Configuration) i.next();
            final String componentName = componentConfig.getName();

            // Find the role
            String role = componentConfig.getAttribute("role", null);
            String alias = null;
            if (role == null) {
                // Get the role from the role manager if not explicitely specified
                role = (String) this.configInfo.getShorthands().get(componentName);
                alias = componentName;
                if (role == null) {
                    // Unknown role
                    throw new ConfigurationException("Unknown component type '" + componentName +
                                                     "' at " + componentConfig.getLocation());
                }
            }

            // Find the className
            String className = componentConfig.getAttribute("class", null);
            // If it has a "name" attribute, add it to the role (similar to the
            // declaration within a service selector)
            // Note: this has to be done *after* finding the className above as we change the role
            String name = componentConfig.getAttribute("name", null);
            ComponentInfo info;
            if (className == null) {
                // Get the default class name for this role
                info = this.configInfo.getRole(role);
                if (info == null) {
                    if (this.configInfo.getComponents().get(role) != null) {
                        throw new ConfigurationException("Duplicate component definition for role " + role + " at " + componentConfig.getLocation());
                    }
                    throw new ConfigurationException("Cannot find a class for role " + role + " at " + componentConfig.getLocation());
                }

                className = info.getComponentClassName();
                if (name != null) {
                    info = info.copy();
                } else if (!className.endsWith("Selector")) {
                    this.configInfo.removeRole(role);
                }
            } else {
                info = new ComponentInfo();
                if (!className.endsWith("Selector")) {
                    this.configInfo.removeRole(role);
                }
            }

            // check for name attribute
            // Note: this has to be done *after* finding the className above as we change the role
            if (name != null) {
                role = role + "/" + name;
                if (alias != null) {
                    alias = alias + '-' + name;
                }
            }

            info.fill(componentConfig);
            info.setComponentClassName(className);
            info.setRole(role);
            if (alias != null) {
                info.setAlias(alias);
            }
            info.setConfiguration(componentConfig);

            final boolean isSelector = className.endsWith("Selector");
            if (!isSelector && this.configInfo.getComponents().get(role) != null) {
                // we now have a duplicate definition which we explictly allow to make
                // overriding of pre defined components possible
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Duplicate component definition for role " + role +
                                      " at " + componentConfig.getLocation() + ". Component " +
                                      "has already been defined at " + ((ComponentInfo) this.configInfo.getComponents().get(role)).getConfiguration().getLocation());
                }
            }

            this.configInfo.addComponent(info);

            // now if this is a selector, then we have to register the single components
            if (info.getConfiguration() != null && className.endsWith("Selector")) {
                String classAttribute = null;
                if (className.equals("org.apache.cocoon.core.container.DefaultServiceSelector")) {
                    classAttribute = "class";
                } else if (className.equals("org.apache.cocoon.components.treeprocessor.sitemap.ComponentsSelector")) {
                    classAttribute = "src";
                }

                if (classAttribute == null) {
                    this.logger.warn("Found unknown selector type (continuing anyway: " + className);
                } else {
                    String componentRole = role;
                    if (componentRole.endsWith("Selector")) {
                        componentRole = componentRole.substring(0, componentRole.length() - 8);
                    }
                    componentRole += '/';
                    Configuration[] children = info.getConfiguration().getChildren();
                    final Map hintConfigs = (Map) this.configInfo.getKeyClassNames().get(role);
                    for (int j = 0; j < children.length; j++) {
                        final Configuration current = children[j];
                        final ComponentInfo childInfo = new ComponentInfo();
                        childInfo.fill(current);
                        childInfo.setConfiguration(current);
                        final ComponentInfo hintInfo = hintConfigs == null ? null
                                                                           : (ComponentInfo) hintConfigs.get(current.getName());
                        if (current.getAttribute(classAttribute, null) != null || hintInfo == null) {
                            childInfo.setComponentClassName(current.getAttribute(classAttribute));
                        } else {
                            childInfo.setComponentClassName(hintInfo.getComponentClassName());
                        }
                        childInfo.setRole(componentRole + current.getAttribute("name"));
                        this.configInfo.addComponent(childInfo);
                    }
                }
            }
        }
    }

    /**
     * Handle includes of avalon configurations.
     *
     * @param contextURI
     * @param loadedURIs
     * @param includeStatement
     * @throws ConfigurationException
     */
    protected void handleInclude(final String        contextURI,
                                 final Set           loadedURIs,
                                 final Configuration includeStatement)
    throws ConfigurationException {
        final String includeURI = includeStatement.getAttribute("src", null);
        String directoryURI = null;
        if (includeURI == null) {
            // check for directories
            directoryURI = includeStatement.getAttribute("dir", null);
        }
        if (includeURI == null && directoryURI == null) {
            throw new ConfigurationException("Include statement must either have a 'src' or 'dir' attribute, at " +
                                             includeStatement.getLocation());
        }

        if (includeURI != null) {
            try {
                Resource src = this.resolver.getResource(getUrl(includeURI, contextURI));
                loadURI(src, loadedURIs, includeStatement);
            } catch (Exception e) {
                throw new ConfigurationException("Cannot load '" + includeURI + "' at " +
                                                 includeStatement.getLocation(), e);
            }

        } else {
            boolean load = true;
            // test if directory exists (only if not classpath protocol is used)
            if (!ResourceUtils.isClasspathUri(directoryURI)) {
                Resource dirResource = this.resolver.getResource(this.getUrl(directoryURI, contextURI));
                if (!dirResource.exists()) {
                    if (!includeStatement.getAttributeAsBoolean("optional", false)) {
                        throw new ConfigurationException("Directory '" + directoryURI + "' does not exist (" + includeStatement.getLocation() + ").");
                    }
                    load = false;
                }
            }
            if (load) {
                final String pattern = includeStatement.getAttribute("pattern", null);
                try {
                    Resource[] resources = this.resolver.getResources(this.getUrl(directoryURI + '/' + pattern, contextURI));
                    if (resources != null) {
                        Arrays.sort(resources, ResourceUtils.getResourceComparator());
                        for (int i = 0; i < resources.length; i++) {
                            loadURI(resources[i], loadedURIs, includeStatement);
                        }
                    }
                } catch (Exception e) {
                    throw new ConfigurationException("Cannot load from directory '" + directoryURI + "' at " + includeStatement.getLocation(), e);
                }
            }
        }
    }

    protected void loadURI(final Resource      src,
                           final Set           loadedURIs,
                           final Configuration includeStatement)
    throws ConfigurationException, IOException {
        // If already loaded: do nothing
        final String uri = correctUrl(getUrl(src));
        if (!loadedURIs.contains(uri)) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Loading configuration: " + uri);
            }
            // load it and store it in the read set
            Configuration includeConfig;
            try {
                DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(true);
                includeConfig = builder.build(src.getInputStream(), uri);
            } catch (Exception e) {
                throw new ConfigurationException("Cannot load '" + uri + "' at " + includeStatement.getLocation(), e);
            }
            loadedURIs.add(uri);

            // what is it?
            final String includeKind = includeConfig.getName();
            if (includeKind.equals("components") || includeKind.equals("cocoon")) {
                // more components
                this.parseConfiguration(includeConfig, uri, loadedURIs);
            } else if (includeKind.equals("role-list")) {
                // more roles
                this.configureRoles(includeConfig);
            } else {
                throw new ConfigurationException("Unknow document '" + includeKind + "' included at " +
                                                 includeStatement.getLocation());
            }
        }
    }

    /**
     * Handle include for spring bean configurations.
     *
     * @param contextURI
     * @param includeStatement
     * @throws ConfigurationException
     */
    protected void handleBeanInclude(final String contextURI,
                                     final Configuration includeStatement)
    throws ConfigurationException {
        final String includeURI = includeStatement.getAttribute("src", null);
        String directoryURI = null;
        if (includeURI == null) {
            // check for directories
            directoryURI = includeStatement.getAttribute("dir", null);
        }
        if (includeURI == null && directoryURI == null) {
            throw new ConfigurationException(
                    "Include statement must either have a 'src' or 'dir' attribute, at "
                            + includeStatement.getLocation());
        }

        if (includeURI != null) {
            try {
                Resource src = this.resolver.getResource(getUrl(includeURI, contextURI));
                this.configInfo.addImport(getUrl(src));
            } catch (Exception e) {
                throw new ConfigurationException("Cannot load '" + includeURI + "' at " +
                                                 includeStatement.getLocation(), e);
            }

        } else {
            // test if directory exists
            Resource dirResource = this.resolver.getResource(this.getUrl(directoryURI, contextURI));
            if ( dirResource.exists() ) {
                final String pattern = includeStatement.getAttribute("pattern", null);
                try {
                    Resource[] resources = this.resolver.getResources(this.getUrl(directoryURI + '/' + pattern, contextURI));
                    if ( resources != null ) {
                        Arrays.sort(resources, ResourceUtils.getResourceComparator());
                        for(int i=0; i < resources.length; i++) {
                           this.configInfo.addImport(getUrl(resources[i]));
                        }
                    }
                } catch (IOException ioe) {
                    throw new ConfigurationException("Unable to read configurations from "
                            + directoryURI);
                }
            } else {
                if (!includeStatement.getAttributeAsBoolean("optional", false)) {
                    throw new ConfigurationException("Directory '" + directoryURI + "' does not exist (" +
                                                     includeStatement.getLocation() + ").");
                }
            }
        }
    }

    /**
     * Reads a configuration object and creates the role, shorthand,
     * and class name mapping.
     *
     * @param configuration  The configuration object.
     * @throws ConfigurationException if the configuration is malformed
     */
    protected final void configureRoles( final Configuration configuration )
    throws ConfigurationException {
        final Configuration[] roles = configuration.getChildren();
        for (int i = 0; i < roles.length; i++) {
            final Configuration role = roles[i];

            if ("alias".equals(role.getName())) {
                final String roleName = role.getAttribute("role");
                final String shorthand = role.getAttribute("shorthand");
                this.configInfo.getShorthands().put(shorthand, roleName);
                continue;
            }
            if (!"role".equals(role.getName())) {
                throw new ConfigurationException("Unexpected '" + role.getName() + "' element at " + role.getLocation());
            }

            final String roleName = role.getAttribute("name");
            final String shorthand = role.getAttribute("shorthand", null);
            final String defaultClassName = role.getAttribute("default-class", null);

            if (shorthand != null) {
                // Store the shorthand and check that its consistent with any previous one
                Object previous = this.configInfo.getShorthands().put(shorthand, roleName);
                if (previous != null && !previous.equals(roleName)) {
                    throw new ConfigurationException("Shorthand '" + shorthand + "' already used for role " +
                                                     previous + ": inconsistent declaration at " + role.getLocation());
                }
            }

            if (defaultClassName != null) {
                ComponentInfo info = this.configInfo.getRole(roleName);
                if (info == null) {
                    // Create a new info and store it
                    info = new ComponentInfo();
                    info.setComponentClassName(defaultClassName);
                    info.fill(role);
                    info.setRole(roleName);
                    info.setConfiguration(role);
                    info.setAlias(shorthand);
                    this.configInfo.addRole(roleName, info);
                } else {
                    // Check that it's consistent with the existing info
                    if (!defaultClassName.equals(info.getComponentClassName())) {
                        throw new ConfigurationException("Invalid redeclaration: default class already set to " + info.getComponentClassName() +
                                                         " for role " + roleName + " at " + role.getLocation());
                    }
                    //FIXME: should check also other ServiceInfo members
                }
            }

            final Configuration[] keys = role.getChildren("hint");
            if (keys.length > 0) {
                Map keyMap = (Map) this.configInfo.getKeyClassNames().get(roleName);
                if (keyMap == null) {
                    keyMap = new HashMap();
                    this.configInfo.getKeyClassNames().put(roleName, keyMap);
                }

                for (int j = 0; j < keys.length; j++) {
                    Configuration key = keys[j];

                    final String shortHand = key.getAttribute("shorthand").trim();
                    final String className = key.getAttribute("class").trim();

                    ComponentInfo info = (ComponentInfo) keyMap.get(shortHand);
                    if (info == null) {
                        info = new ComponentInfo();
                        info.setComponentClassName(className);
                        info.fill(key);
                        info.setConfiguration(key);
                        info.setAlias(shortHand);
                        keyMap.put(shortHand, info);
                    } else {
                        // Check that it's consistent with the existing info
                        if (!className.equals(info.getComponentClassName())) {
                            throw new ConfigurationException("Invalid redeclaration: class already set to " + info.getComponentClassName() +
                                                             " for hint " + shortHand + " at " + key.getLocation());
                        }
                        //FIXME: should check also other ServiceInfo members
                    }
                }
            }
        }
    }
}
TOP

Related Classes of org.apache.cocoon.core.container.spring.avalon.ConfigurationReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.