All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.shale.dialog.scxml.config.ConfigurationParser Maven / Gradle / Ivy

The newest version!
/*
 * 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.shale.dialog.scxml.config;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.digester.Digester;
import org.apache.commons.digester.Rule;
import org.apache.commons.scxml.env.SimpleErrorHandler;
import org.apache.commons.scxml.io.SCXMLDigester;
import org.apache.commons.scxml.model.CustomAction;
import org.apache.commons.scxml.model.ModelException;
import org.apache.commons.scxml.model.SCXML;
import org.apache.shale.dialog.scxml.Globals;
import org.apache.shale.dialog.scxml.action.RedirectAction;
import org.apache.shale.dialog.scxml.action.ViewAction;
import org.apache.shale.dialog.scxml.config.DialogMetadata.SCXMLAction;
import org.xml.sax.SAXException;

/**
 * 

Configuration utility for parsing SCXML documents as resources for * defining dialogs. This class has no dependencies on web tier APIs, * only on the Commons SCXML state machine engine library, and * on the parsing technology (Commons Digester) being used.

* *

The configuration for each Shale dialog exists as a standalone * SCXML document, with additional dialog "metadata" file(s) * that serve as the entry point for the Shale Dialog Manager.

* *

These dialog-config.xml file(s) look like this: *

 * <dialogs>
 *     <dialog name="Foo" scxmlconfig="foo.scxml" />
 *     <dialog name="Bar" scxmlconfig="bar.scxml"
 *             dataclassname="org.apache.shale.examples.Bar" />
 *     <dialog name="Baz" scxmlconfig="baz.scxml" />
 *     <!-- etc. -->
 * </dialogs>
 * 
*

* *

To use this utility, instantiate a new instance and set the * dialogs, resource, and validating * properties. Then, call the parse() method. You can parse * more than one resource by resetting the resource * property and calling parse() again.

* * @since 1.0.4 */ public final class ConfigurationParser { // -------------------------------------------------------------- Properties /** *

Registration information for the DTD we will use to validate.

*/ private static final String[] REGISTRATIONS = { "-//Apache Software Foundation//DTD Shale SCXML Dialog Configuration 1.0//EN", "/org/apache/shale/dialog/scxml/dialog-scxml-config_1_0.dtd" }; /** *

Map of Dialog instances resulting * from parsing, keyed by dialog name.

*/ private Map dialogs = null; /** *

Return the Map of Dialog instances * into which parsed information will be stored, keyed by dialog * name.

* * @return Map of SCXML instances, keyed by logical dialog name */ public Map getDialogs() { return this.dialogs; } /** *

Set the Map of Dialog instances * into which parsed information will be stored, keyed by dialog * name.

* * @param dialogs The new map */ public void setDialogs(Map dialogs) { this.dialogs = dialogs; } /** *

The URL of the configuration resource to be parsed.

*/ private URL resource = null; /** *

Return the URL of the configuration resource to be parsed.

* * @return The resource URL */ public URL getResource() { return this.resource; } /** *

Set the URL of the configuration resource to be parsed.

* * @param resource The new resource URL */ public void setResource(URL resource) { this.resource = resource; } /** *

Flag indicating whether we should do a validating parse or not.

*/ private boolean validating = true; /** *

Return a flag indicating whether we will be doing a validating parse * or not. Default value is false.

* * @return Whether the parse is validating */ public boolean isValidating() { return this.validating; } /** *

Set a flag indicating whether we will be doing a validating parse * or not.

* * @param validating New flag value */ public void setValidating(boolean validating) { this.validating = validating; } // ---------------------------------------------------------- Public Methods /** *

Parse the configuration resource identified by the resource * property, storing resulting information in the Map specified * by the dialogs property.

* * @exception IOException if an input/output error occurs * @exception SAXException if an XML parsing error occurs */ public void parse() throws IOException, SAXException { Map metadata = new HashMap(); Digester digester = digester(); digester.clear(); digester.push(metadata); digester.parse(getResource()); parseDialogs(metadata); } // --------------------------------------------------------- Private Methods /** *

Return a fully configured Digester instance.

* * @return The configuration parser Digester instance */ private Digester digester() { Digester digester = new Digester(); // Configure global characteristics digester.setNamespaceAware(false); digester.setUseContextClassLoader(true); digester.setValidating(isValidating()); // Register local copy of our DTDs for (int i = 0; i < REGISTRATIONS.length; i += 2) { URL url = this.getClass().getResource(REGISTRATIONS[i + 1]); digester.register(REGISTRATIONS[i], url); } // Configure processing rules // dialogs/dialog digester.addObjectCreate("dialogs/dialog", DialogMetadata.class); digester.addSetProperties("dialogs/dialog"); digester.addRule("dialogs/dialog", new AddDialogMetadataRule()); digester.addObjectCreate("dialogs/dialog/scxmlaction", SCXMLAction.class); digester.addSetProperties("dialogs/dialog/scxmlaction"); digester.addSetNext("dialogs/dialog/scxmlaction", "addDialogAction"); return digester; } /** *

Parse the SCXML documents in the dialog metadata, storing resulting * information as an entry in the Map specified by the * dialogs property.

* * @param metadata The metadata map * @throws IOException if an input/output error occurs * @throws SAXException if an XML parsing error occurs */ private void parseDialogs(Map metadata) throws IOException, SAXException { Iterator iterator = metadata.entrySet().iterator(); // Create a list of the custom Commons SCXML actions defined by the // Shale dialog Commons SCXML implementation List shaleDialogActions = new ArrayList(); // CustomAction redirectAction = new CustomAction(Globals.CUSTOM_SCXML_ACTIONS_URI, "redirect", RedirectAction.class); shaleDialogActions.add(redirectAction); // CustomAction viewAction = new CustomAction(Globals.CUSTOM_SCXML_ACTIONS_URI, "view", ViewAction.class); shaleDialogActions.add(viewAction); // Class loader for app developer defined custom Commons SCXML actions ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = ConfigurationParser.class.getClassLoader(); } while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); String name = (String) entry.getKey(); DialogMetadata dMetadata = (DialogMetadata) entry.getValue(); String scxmlconfig = dMetadata.getScxmlconfig(); // The custom actions available to this dialog is the // summation of the ones defined by this Shale dialog module // and those defined by the app developer using the dialog // configuration file for this dialog List customDialogActions = new ArrayList(); customDialogActions.addAll(shaleDialogActions); List devActions = dMetadata.getDialogActions(); for (int i = 0; i < devActions.size(); i++) { SCXMLAction scxmlAction = (SCXMLAction) devActions.get(i); String actionname = scxmlAction.getName(); String uri = scxmlAction.getUri(); String actionFQCN = scxmlAction.getActionclassname(); if (actionname == null || uri == null || actionFQCN == null) { // shouldn't happen if dialog-config is validated throw new IllegalArgumentException("A custom Commons" + " SCXML action ( element) in the" + " dialog configuration is missing the 'name'," + " 'uri' or 'actionclassname'"); } Class customActionClass = null; try { customActionClass = loader.loadClass(actionFQCN); } catch (Exception e) { throw new IllegalArgumentException("Cannot load " + "custom Commons SCXML action class '" + actionFQCN + "' for action with name '" + actionname + "'"); } CustomAction customAction = new CustomAction(uri, actionname, customActionClass); customDialogActions.add(customAction); } URL resource = new URL(getResource(), scxmlconfig); SCXML dialog = null; try { // Parse document, with rules for custom actions in place dialog = SCXMLDigester.digest(resource, new SimpleErrorHandler(), customDialogActions); } catch (ModelException me) { throw new SAXException(me.getMessage(), me); } dMetadata.setStateMachine(dialog); dialogs.put(name, dMetadata); } } // -------------------------------------------- Private Rule Implementations /** *

Custom Digester rule to add a dialog.

*/ static class AddDialogMetadataRule extends Rule { /** * Constructor. */ public AddDialogMetadataRule() { super(); } /** * {@inheritDoc} * * @see Rule#end(String,String) */ public void end(String namespace, String name) throws Exception { DialogMetadata dialog = (DialogMetadata) getDigester().peek(); Map map = (Map) getDigester().peek(1); map.put(dialog.getName(), dialog); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy