View Javadoc

1   /*
2    * $Id: XmlCommandFactory.java 269 2005-08-10 17:49:22Z josem $
3    *
4    * Tarsis
5    * Copyright (C) 2002 Talika Open Source Group
6    *
7    * This program is free software; you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation; either version 2 of the License, or
10   * (at your option) any later version.
11   *
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with this program; if not, write to the Free Software
19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   *
21   */
22  
23  package org.talika.tarsis.command.factory.xml;
24  
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.util.HashSet;
28  import java.util.Map;
29  
30  import javax.xml.parsers.ParserConfigurationException;
31  import javax.xml.parsers.SAXParserFactory;
32  
33  import org.talika.tarsis.command.Command;
34  import org.talika.tarsis.command.factory.CommandFactoryService;
35  import org.talika.tarsis.context.Context;
36  import org.talika.tarsis.log.Logger;
37  import org.talika.tarsis.service.ServiceException;
38  import org.xml.sax.InputSource;
39  import org.xml.sax.SAXException;
40  import org.xml.sax.SAXParseException;
41  import org.xml.sax.XMLReader;
42  
43  /**
44   * XML based implementation of <code>CommandFactory</code> interface.<br>
45   * <br>
46   * It reads command definitions from an XML file. It supports multiple definitions
47   * files stored in a package directory, typically <code>/WEB-INF/packages/</code>.<br>
48   * <br>
49   * File name is considered to be package name and to have all commands of a single
50   * package.<br>
51   * Commands packages are loaded on demand when they are invoked. When a command
52   * is lodaded is initialized and its action is put into service.
53   *
54   * @author  Jose M. Palomar
55   * @version $Revision: 269 $
56   * @see XmlCommandsFactory
57   */
58  public final class XmlCommandFactory extends CommandFactoryService {
59  
60      // Constants
61      /**
62       * Default directory for package files.
63       */
64      public static final String PACKAGES_DIR = "/WEB-INF/packages/";
65  
66      /**
67       * Default package file extension.
68       */
69      public static final String PACKAGE_EXTENSION = "xml";
70  
71      // Fields
72      /**
73       * Packages directory path.
74       */
75      private String packagesDir;
76  
77      /**
78       * Loaded packages <code>Set</code>.
79       */
80      private HashSet packsLoaded;
81  
82      // Constructors
83      /**
84       * Creates a new <code>XmlCommandFactory</code> object.
85       */
86      public XmlCommandFactory() {
87      }
88  
89      /**
90       * Called by the framework to indicate that is being placed into service.<br>
91       * <br>
92       * Initialization secuence:
93       * <ol>
94       * <li> Call super <code>init</code> method.</li>
95       * <li> Checks if <code>packagesDir</code> is set if not sets its value to
96       * <code>PACKAGES_DIR</code>.</li>
97       * <li> Obtains <code>Logger</code> instance from context.</li>
98       * <li> Initializes <code>packsLoaded</code> to an empty <code>Set</code>.</li>
99       * </ol>
100      *
101      * @param context Context context that initialized service.
102      * @throws ServiceException if an exception has occurred that interferes with the
103      * services's normal operation
104      * @see org.talika.tarsis.service.Service#init(Context)
105      *
106      * @todo Should check packages directory existence.
107      */
108     public void init(Context context) throws ServiceException {
109         super.init(context);
110 
111         if (this.packagesDir == null) {
112             this.packagesDir = PACKAGES_DIR;
113         }
114         else if (!this.packagesDir.endsWith("/")) {
115             this.packagesDir = this.packagesDir + "/";
116         }
117 
118         // FIXME: Should check directory existence.
119 
120         this.packsLoaded = new HashSet();
121 
122     }
123 
124     /**
125      * Called by the framework to indicate that is being placed out of service.
126      * <br>
127      * Destroy secuence:
128      * <ol>
129      * <li> Clears <code>packsLoaded</code>.</li>
130      * <li> Call super <code>destroy</code> method.</li>
131      * </ol>
132      *
133      * @see org.talika.tarsis.service.Service#destroy()
134      */
135     public void destroy() {
136 
137         this.packsLoaded.clear();
138         this.packsLoaded = null;
139 
140         super.destroy();
141 
142     }
143 
144     /**
145      * Service name.
146      *
147      * @return String service name.
148      * @see org.talika.tarsis.service.Service#getName()
149      */
150     public String getName() {
151         return "XmlCommandFactory";
152     }
153 
154     /**
155      * Tries to load command definition from repository for a given name.<br>
156      * <br>
157      * Loading a command causes loading its entire package.
158      *
159      * @param commandName String name of command to load.
160      */
161     protected void loadCommand(String commandName) {
162 
163         if (getLogger().isInfoEnabled()) {
164             getLogger().logInfo("Loading command " + commandName);
165         }
166 
167         loadPackage(commandName.substring(0, commandName.lastIndexOf(Command.PACKAGE_SEPARATOR)));
168 
169     }
170 
171     /**
172      * Tries to load all command definitions from repository for a given package.<br>
173      * Packages are loaded only once.
174      *
175      * @param packageName String name for package to load.
176      */
177     protected void loadPackage(String packageName) {
178 
179         // Prevent reloading packages
180         if (packsLoaded.contains(packageName)) {
181             return;
182         }
183 
184         if (getLogger().isInfoEnabled()) {
185             getLogger().logInfo("Loading package " + packageName);
186         }
187 
188         String packageFilePath = this.packagesDir + packageName + "." + PACKAGE_EXTENSION;
189         InputStream input = getContext().getResourceAsStream(packageFilePath);
190         if (input != null) {
191             Map commands = loadXmlPackage(new InputSource(input));
192             if (commands != null) {
193                 addCommands(commands);
194                 packsLoaded.add(packageName);
195             }
196             else {
197                 if (getLogger().isWarningEnabled()) {
198                     getLogger().logWarning("Error loading package " + packageName);
199                 }
200             }
201         }
202         else {
203             if (getLogger().isWarningEnabled()) {
204                 getLogger().logWarning("Package not found " + packageName);
205             }
206         }
207 
208     }
209 
210     /**
211      * Loads command definitions from an XML source.
212      *
213      * @param input InputSource XML source.
214      * @return Map a map with all package commands or <code>null</code> if something
215      * wrong happens parsing XML source.
216      */
217     protected Map loadXmlPackage(InputSource input) {
218 
219         try {
220 
221             SAXParserFactory spf = SAXParserFactory.newInstance();
222             spf.setValidating(true);
223             spf.setNamespaceAware(false);
224 
225             XMLReader parser = null;
226             parser = spf.newSAXParser().getXMLReader();
227             XmlCommandPackageHandler handler = new XmlCommandPackageHandler();
228             parser.setContentHandler(handler);
229             parser.setErrorHandler(handler);
230             parser.setEntityResolver(handler);
231             parser.parse(input);
232 
233             return handler.getCommands();
234 
235         }
236         catch (SAXParseException spe) {
237             if (getLogger().isDebugEnabled()) {
238                 getLogger().logDebug("Error parsing package (" + spe.getMessage() + ")");
239             }
240         }
241         catch (SAXException se) {
242             if (se.getException() != null) {
243                 if (getLogger().isDebugEnabled()) {
244                     getLogger().logDebug("Error parsing package (" + se.getException().getMessage() + ")");
245                 }
246             }
247             else {
248                 if (getLogger().isDebugEnabled()) {
249                     getLogger().logDebug("Error parsing package (" + se.getMessage() + ")");
250                 }
251             }
252         }
253         catch (ParserConfigurationException pce) {
254             if (getLogger().isDebugEnabled()) {
255                 getLogger().logDebug("Error parsing package (" + pce.getMessage() + ")");
256             }
257         }
258         catch (IOException ioe) {
259             if (getLogger().isDebugEnabled()) {
260                 getLogger().logDebug("Error parsing package (" + ioe.getMessage() + ")");
261             }
262         }
263 
264         return null;
265 
266     }
267 
268     /**
269      * Returns packages directory.
270      *
271      * @return String packages directory.
272      */
273     public String getPackakesDir() {
274         return packagesDir;
275     }
276 
277     /**
278      * Sets packages directory.
279      *
280      * @param packagesDir String sets packages directory.
281      */
282     public void setPackagesDir(String packagesDir) {
283         this.packagesDir = packagesDir;
284     }
285 
286 }