| /* |
| * 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.components.validation.impl; |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.apache.avalon.framework.activity.Disposable; |
| import org.apache.avalon.framework.activity.Initializable; |
| import org.apache.avalon.framework.activity.Startable; |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.configuration.ConfigurationException; |
| import org.apache.avalon.framework.context.Context; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.context.Contextualizable; |
| import org.apache.avalon.framework.logger.LogEnabled; |
| import org.apache.avalon.framework.logger.Logger; |
| import org.apache.avalon.framework.parameters.Parameterizable; |
| import org.apache.avalon.framework.parameters.Parameters; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceSelector; |
| import org.apache.avalon.framework.service.Serviceable; |
| import org.apache.avalon.framework.thread.ThreadSafe; |
| import org.apache.cocoon.components.validation.SchemaParser; |
| |
| /** |
| * <p>The default implementation of the {@link Validator} interface provides |
| * core management for a number of {@link SchemaParser} instances.</p> |
| * |
| * <p>Given the simplicity of this implementation, only {@link SchemaParser}s |
| * implementing the {@link ThreadSafe} interface can be managed, and they can be |
| * accessed directly (via its name) through the methods specified by the |
| * {@link ServiceSelector} interface.</p> |
| * |
| * <p>That said, normally selection would occur using the methods declared by the |
| * {@link AbstractValidator} class and implemented here.</p> |
| * |
| */ |
| public class DefaultValidator extends AbstractValidator implements ServiceSelector, |
| ThreadSafe, Contextualizable, Initializable, Disposable, Configurable { |
| |
| /** <p>A {@link Map} associating {@link SchemaParser}s with their names.</p> */ |
| private final Map components = Collections.synchronizedMap(new HashMap()); |
| /** <p>A {@link Map} associating component names with grammars.</p> */ |
| private final Map grammars = Collections.synchronizedMap(new HashMap()); |
| |
| /** <p>The configured {@link Context} instance.</p> */ |
| private Context context = null; |
| /** <p>The configured {@link Configuration} instance.</p> */ |
| private Configuration conf = null; |
| |
| /** |
| * <p>Create a new {@link DefaultValidator} instance.</p> |
| */ |
| public DefaultValidator() { |
| super(); |
| } |
| |
| /** |
| * <p>Contextualize this instance.</p> |
| */ |
| public void contextualize(Context context) |
| throws ContextException { |
| this.context = context; |
| } |
| |
| /** |
| * <p>Configure this instance.</p> |
| */ |
| public void configure(Configuration conf) |
| throws ConfigurationException { |
| this.conf = conf; |
| } |
| |
| /** |
| * <p>Initialize this instance.</p> |
| */ |
| public void initialize() |
| throws Exception { |
| this.logger.debug("Initializing " + this.getClass().getName()); |
| |
| if (this.logger == null) throw new IllegalStateException("Null logger"); |
| if (this.context == null) throw new IllegalStateException("Null context"); |
| if (this.manager == null) throw new IllegalStateException("Null manager"); |
| if (this.conf == null) throw new IllegalStateException("Null configuration"); |
| |
| Configuration configurations[] = this.conf.getChildren("schema-parser"); |
| this.logger.debug("Configuring " + configurations.length + " schema parsers" |
| + " from " + this.conf.getLocation()); |
| |
| /* Iterate through all the sub-confiuration instances */ |
| for (int x = 0; x < configurations.length; x++) try { |
| final Configuration configuration = configurations[x]; |
| final String className = configuration.getAttribute("class"); |
| final String selectionKey = configuration.getAttribute("name"); |
| |
| /* Check that we don't have a duplicate schema parser name in configs */ |
| if (this.components.containsKey(selectionKey)) { |
| String message = "Duplicate schema parser \"" + selectionKey + "\""; |
| throw new ConfigurationException(message, configuration); |
| } |
| |
| /* Dump some debugging information, just in case */ |
| this.logger.debug("Configuring schema parser " + selectionKey + " as " |
| + className + " from " + configuration.getLocation()); |
| |
| /* Try to load and instantiate the SchemaParser */ |
| final SchemaParser schemaParser; |
| try { |
| /* Load the class */ |
| final Class clazz = Class.forName(className); |
| |
| /* ClassCastExceptions normally don't come with messages (darn) */ |
| if (! SchemaParser.class.isAssignableFrom(clazz)) { |
| String message = "Class " + className + " doesn't implement the " |
| + SchemaParser.class.getName() + " interface"; |
| throw new ConfigurationException(message, configuration); |
| } |
| |
| /* We only support ThreadSafe SchemaParser instances */ |
| if (! ThreadSafe.class.isAssignableFrom(clazz)) { |
| String message = "Class " + className + " doesn't implement the " |
| + ThreadSafe.class.getName() + " interface"; |
| throw new ConfigurationException(message, configuration); |
| } |
| |
| /* Instantiate and set up the new SchemaParser */ |
| schemaParser = (SchemaParser) clazz.newInstance(); |
| this.setupComponent(selectionKey, schemaParser, configuration); |
| |
| } catch (ConfigurationException exception) { |
| throw exception; |
| } catch (Exception exception) { |
| String message = "Unable to instantiate SchemaParser " + className; |
| throw new ConfigurationException(message, configuration, exception); |
| } |
| |
| /* Store this instance (and report about it) */ |
| this.components.put(selectionKey, schemaParser); |
| this.logger.debug("SchemaParser \"" + selectionKey + "\" instantiated" + |
| " from class " + className); |
| |
| /* Analyze the grammars provided by the current SchemaParser */ |
| String grammars[] = schemaParser.getSupportedGrammars(); |
| if (grammars == null) continue; |
| |
| /* Iterate through the grammars and store them (default lookup) */ |
| for (int k = 0; k < grammars.length; k++) { |
| if (this.grammars.containsKey(grammars[k])) { |
| if (this.logger.isDebugEnabled()) { |
| this.logger.debug("SchemaParser \"" + selectionKey + "\" " + |
| "supports grammar \"" + grammars[k] + |
| "\" but is not the default provider"); |
| } |
| continue; |
| } |
| |
| /* Noone yet supports this grammar, make this the default */ |
| this.grammars.put(grammars[k], selectionKey); |
| if (this.logger.isDebugEnabled()) { |
| this.logger.debug("SchemaParser \"" + selectionKey + "\" is the " |
| + "default grammar provider for "+grammars[k]); |
| } |
| } |
| |
| } catch (Exception exception) { |
| /* Darn, we had an exception instantiating one of the components */ |
| exception.printStackTrace(); |
| this.logger.fatalError("Exception creating schema parsers", exception); |
| |
| /* Dispose all previously stored component instances */ |
| Iterator iterator = this.components.values().iterator(); |
| while (iterator.hasNext()) try { |
| this.decommissionComponent(iterator.next()); |
| } catch (Exception nested) { |
| this.logger.fatalError("Error decommissioning component", nested); |
| } |
| |
| /* Depending on the exception type, re-throw it or wrap it */ |
| if (exception instanceof ConfigurationException) { |
| throw exception; |
| } else { |
| Configuration configuration = configurations[x]; |
| String message = "Unable to setup SchemaParser declared at "; |
| message += configuration.getLocation(); |
| throw new ConfigurationException(message, configuration, exception); |
| } |
| } |
| } |
| |
| /** |
| * <p>Dispose of this instance.</p> |
| * |
| * <p>All sub-components initialized previously will be disposed of when this |
| * method is called.</p> |
| */ |
| public void dispose() { |
| Iterator iterator = this.components.values().iterator(); |
| while (iterator.hasNext()) try { |
| this.decommissionComponent(iterator.next()); |
| } catch (Exception exception) { |
| this.logger.fatalError("Error decommissioning component", exception); |
| } |
| } |
| |
| /* =========================================================================== */ |
| /* IMPLEMENTATION OF METHODS SPECIFIED BY THE ABSTRACTVALIDATOR CLASS */ |
| /* =========================================================================== */ |
| |
| /** |
| * <p>Attempt to acquire a {@link SchemaParser} interface able to understand |
| * the grammar language specified.</p> |
| * |
| * @param grammar the grammar language that must be understood by the returned |
| * {@link SchemaParser} |
| * @return a {@link SchemaParser} instance or <b>null</b> if none was found able |
| * to understand the specified grammar language. |
| */ |
| protected SchemaParser lookupParserByGrammar(String grammar) { |
| if (this.grammars.containsKey(grammar)) { |
| return this.lookupParserByName((String) this.grammars.get(grammar)); |
| } |
| return null; |
| } |
| |
| /** |
| * <p>Attempt to acquire a {@link SchemaParser} interface associated with the |
| * specified instance name.</p> |
| * |
| * @param name the name associated with the {@link SchemaParser} to be returned. |
| * @return a {@link SchemaParser} instance or <b>null</b> if none was found. |
| */ |
| protected SchemaParser lookupParserByName(String name) { |
| if (this.isSelectable(name)) try { |
| return (SchemaParser) this.select(name); |
| } catch (ServiceException exception) { |
| return null; |
| } |
| return null; |
| } |
| |
| /** |
| * <p>Release a previously acquired {@link SchemaParser} instance back to its |
| * original component manager.</p> |
| * |
| * <p>This method is supplied in case solid implementations of this class relied |
| * on the {@link ServiceManager} to manage {@link SchemaParser}s instances.</p> |
| * |
| * @param parser the {@link SchemaParser} whose instance is to be released. |
| */ |
| protected void releaseParser(SchemaParser parser) { |
| this.release(parser); |
| } |
| |
| /* =========================================================================== */ |
| /* IMPLEMENTATION OF THE METHODS SPECIFIED BY THE SERVICESELECTOR INTERFACE */ |
| /* =========================================================================== */ |
| |
| /** |
| * <p>Select a subcomponent ({@link SchemaParser}) associated with the specified |
| * selection key (its configured "name").</p> |
| */ |
| public Object select(Object selectionKey) |
| throws ServiceException { |
| /* Look up for the specified component and return it if found */ |
| if ( this.components.containsKey(selectionKey)) { |
| return this.components.get(selectionKey); |
| } |
| |
| /* Fail miserably */ |
| String message = "No component associated with " + selectionKey; |
| throw new ServiceException((String) selectionKey, message); |
| } |
| |
| /** |
| * <p>Check whether a subcomponent ({@link SchemaParser}) associated with the |
| * specified selection key (its configured "name") is selectable in |
| * this {@link ServiceSelector} instance.</p> |
| */ |
| public boolean isSelectable(Object selectionKey) { |
| return this.components.containsKey(selectionKey); |
| } |
| |
| /** |
| * <p>Release a subcomponent ({@link SchemaParser}) instance previously selected |
| * from this {@link ServiceSelector} instance.</p> |
| */ |
| public void release(Object component) { |
| // We don't need to do anything in this method. |
| } |
| |
| /* =========================================================================== */ |
| /* SUBCOMPONENTS (SCHEMA PARSERS) LIFECYCLE MANAGEMENT METHODS */ |
| /* =========================================================================== */ |
| |
| /** |
| * <p>Manage the instantiation lifecycle of a specified component.</p> |
| */ |
| private Object setupComponent(String name, Object component, Configuration conf) |
| throws Exception { |
| boolean initialized = false; |
| boolean started = false; |
| |
| try { |
| if (component instanceof LogEnabled) { |
| Logger logger = this.logger.getChildLogger(name); |
| ((LogEnabled) component).enableLogging(logger); |
| } |
| |
| if (component instanceof Contextualizable) { |
| ((Contextualizable) component).contextualize(this.context); |
| } |
| |
| if (component instanceof Serviceable) { |
| ((Serviceable) component).service(this.manager); |
| } |
| |
| if (component instanceof Configurable) { |
| ((Configurable) component).configure(conf); |
| } |
| |
| if (component instanceof Parameterizable) { |
| Parameters parameters = Parameters.fromConfiguration(conf); |
| ((Parameterizable) component).parameterize(parameters); |
| } |
| |
| if (component instanceof Initializable) { |
| ((Initializable) component).initialize(); |
| initialized = true; |
| } |
| |
| if (component instanceof Startable) { |
| ((Startable) component).start(); |
| started = true; |
| } |
| |
| return component; |
| |
| } catch (Exception exception) { |
| if ((started) && (component instanceof Startable)) try { |
| ((Startable) component).stop(); |
| } catch (Exception nested) { |
| this.logger.fatalError("Error stopping component", nested); |
| } |
| if ((initialized) && (component instanceof Disposable)) try { |
| ((Disposable) component).dispose(); |
| } catch (Exception nested) { |
| this.logger.fatalError("Error disposing component", nested); |
| } |
| throw exception; |
| } |
| } |
| |
| /** |
| * <p>Manage the distruction lifecycle of a specified component.</p> |
| */ |
| private void decommissionComponent(Object component) |
| throws Exception { |
| try { |
| if (component instanceof Startable) ((Startable) component).stop(); |
| } finally { |
| if (component instanceof Disposable) ((Disposable) component).dispose(); |
| } |
| } |
| } |