1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
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
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
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
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 }