001: /*---------------------------------------------------------------------------*\
002: $Id: MetaPlugIn.java 7041 2007-09-09 01:04:47Z bmc $
003: ---------------------------------------------------------------------------
004: This software is released under a BSD-style license:
005:
006: Copyright (c) 2004-2007 Brian M. Clapper. All rights reserved.
007:
008: Redistribution and use in source and binary forms, with or without
009: modification, are permitted provided that the following conditions are
010: met:
011:
012: 1. Redistributions of source code must retain the above copyright notice,
013: this list of conditions and the following disclaimer.
014:
015: 2. The end-user documentation included with the redistribution, if any,
016: must include the following acknowlegement:
017:
018: "This product includes software developed by Brian M. Clapper
019: (bmc@clapper.org, http://www.clapper.org/bmc/). That software is
020: copyright (c) 2004-2007 Brian M. Clapper."
021:
022: Alternately, this acknowlegement may appear in the software itself,
023: if wherever such third-party acknowlegements normally appear.
024:
025: 3. Neither the names "clapper.org", "curn", nor any of the names of the
026: project contributors may be used to endorse or promote products
027: derived from this software without prior written permission. For
028: written permission, please contact bmc@clapper.org.
029:
030: 4. Products derived from this software may not be called "curn", nor may
031: "clapper.org" appear in their names without prior written permission
032: of Brian M. Clapper.
033:
034: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
036: MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
037: NO EVENT SHALL BRIAN M. CLAPPER BE LIABLE FOR ANY DIRECT, INDIRECT,
038: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
039: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
040: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
041: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
042: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
043: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
044: \*---------------------------------------------------------------------------*/
045:
046: package org.clapper.curn;
047:
048: import org.clapper.curn.parser.RSSChannel;
049:
050: import org.clapper.util.logging.Logger;
051:
052: import java.io.File;
053:
054: import java.net.URLConnection;
055:
056: import java.util.Collection;
057: import java.util.TreeSet;
058:
059: /**
060: * A <tt>MetaPlugIn</tt> object is basically a plug-in that contains all the
061: * loaded plug-ins. It's a singleton that makes it easier for <i>curn</i>
062: * to invoke the various loaded plugins. It is not used outside of
063: * <i>curn</i>. The <tt>MetaPlugIn</tt> singleton object is loaded by an
064: * instance of the {@link PlugInManager} class.
065: *
066: * @see PlugIn
067: * @see PlugInManager
068: * @see CacheLoadedPlugIn
069: * @see FeedConfigItemPlugIn
070: * @see MainConfigItemPlugIn
071: * @see OutputHandlerConfigItemPlugIn
072: * @see PostConfigPlugIn
073: * @see PostFeedDownloadPlugIn
074: * @see PostFeedOutputPlugIn
075: * @see PostFeedParsePlugIn
076: * @see PostOutputHandlerFlushPlugIn
077: * @see PreCacheSavePlugIn
078: * @see PreFeedDownloadPlugIn
079: * @see PreFeedOutputPlugIn
080: * @see ShutdownPlugIn
081: * @see StartupPlugIn
082: * @see UnknownSectionConfigItemPlugIn
083: * @see Curn
084: *
085: * @version <tt>$Revision: 7041 $</tt>
086: */
087: public class MetaPlugIn implements CacheLoadedPlugIn,
088: FeedConfigItemPlugIn, ForceFeedDownloadPlugIn,
089: MainConfigItemPlugIn, OutputHandlerConfigItemPlugIn,
090: PostConfigPlugIn, PostFeedDownloadPlugIn, PostFeedOutputPlugIn,
091: PostFeedParsePlugIn, PostOutputHandlerFlushPlugIn,
092: PreCacheSavePlugIn, PreFeedDownloadPlugIn, PreFeedOutputPlugIn,
093: PostOutputPlugIn, ShutdownPlugIn, StartupPlugIn,
094: UnknownSectionConfigItemPlugIn {
095: /*----------------------------------------------------------------------*\
096: Private Data Items
097: \*----------------------------------------------------------------------*/
098:
099: /**
100: * Plug-in comparator.
101: */
102: private PlugInComparator cmp = new PlugInComparator();
103:
104: /**
105: * The loaded plug-ins, by type.
106: */
107: private final Collection<CacheLoadedPlugIn> cacheLoadedPlugIns = new TreeSet<CacheLoadedPlugIn>(
108: cmp);
109:
110: private final Collection<FeedConfigItemPlugIn> feedConfigItemPlugIns = new TreeSet<FeedConfigItemPlugIn>(
111: cmp);
112:
113: private final Collection<MainConfigItemPlugIn> mainConfigItemPlugIns = new TreeSet<MainConfigItemPlugIn>(
114: cmp);
115:
116: private final Collection<OutputHandlerConfigItemPlugIn> outputHandlerConfigItemPlugIns = new TreeSet<OutputHandlerConfigItemPlugIn>(
117: cmp);
118:
119: private final Collection<PostConfigPlugIn> postConfigPlugIns = new TreeSet<PostConfigPlugIn>(
120: cmp);
121:
122: private final Collection<PostFeedDownloadPlugIn> postFeedDownloadPlugIns = new TreeSet<PostFeedDownloadPlugIn>(
123: cmp);
124:
125: private final Collection<PostFeedOutputPlugIn> postFeedOutputPlugIns = new TreeSet<PostFeedOutputPlugIn>(
126: cmp);
127:
128: private final Collection<PostFeedParsePlugIn> postFeedParsePlugIns = new TreeSet<PostFeedParsePlugIn>(
129: cmp);
130:
131: private final Collection<PostOutputHandlerFlushPlugIn> postOutputHandlerFlushPlugIns = new TreeSet<PostOutputHandlerFlushPlugIn>(
132: cmp);
133:
134: private final Collection<PreCacheSavePlugIn> preCacheSavePlugIns = new TreeSet<PreCacheSavePlugIn>(
135: cmp);
136:
137: private final Collection<ForceFeedDownloadPlugIn> forceFeedDownloadPlugIns = new TreeSet<ForceFeedDownloadPlugIn>(
138: cmp);
139:
140: private final Collection<PreFeedDownloadPlugIn> preFeedDownloadPlugIns = new TreeSet<PreFeedDownloadPlugIn>(
141: cmp);
142:
143: private final Collection<PreFeedOutputPlugIn> preFeedOutputPlugIns = new TreeSet<PreFeedOutputPlugIn>(
144: cmp);
145:
146: private final Collection<PostOutputPlugIn> postOutputPlugIns = new TreeSet<PostOutputPlugIn>(
147: cmp);
148:
149: private final Collection<ShutdownPlugIn> shutdownPlugIns = new TreeSet<ShutdownPlugIn>(
150: cmp);
151:
152: private final Collection<StartupPlugIn> startupPlugIns = new TreeSet<StartupPlugIn>(
153: cmp);
154:
155: private final Collection<UnknownSectionConfigItemPlugIn> unknownSectionConfigItemPlugIns = new TreeSet<UnknownSectionConfigItemPlugIn>(
156: cmp);
157:
158: private final Collection<PlugIn> allPlugIns = new TreeSet<PlugIn>(
159: cmp);
160:
161: /**
162: * The singleton
163: */
164: private static MetaPlugIn metaPlugInWrapper = null;
165:
166: /**
167: * For log messages
168: */
169: private static final Logger log = new Logger(MetaPlugIn.class);
170:
171: /*----------------------------------------------------------------------*\
172: Constructor
173: \*----------------------------------------------------------------------*/
174:
175: /**
176: * Cannot be instantiated normally.
177: */
178: private MetaPlugIn() {
179: // Cannot be instantiated directly.
180: }
181:
182: /*----------------------------------------------------------------------*\
183: Public Methods
184: \*----------------------------------------------------------------------*/
185:
186: /**
187: * Get the <tt>MetaPlugIn</tt> singleton.
188: *
189: * @return the <tt>MetaPlugIn</tt> singleton
190: */
191: public static MetaPlugIn getMetaPlugIn() {
192: assert (metaPlugInWrapper != null);
193: return metaPlugInWrapper;
194: }
195:
196: /**
197: * Add a plug-in to the list of plug-ins wrapped in this object.
198: * This method is only intended for use by the {@link PlugInManager}.
199: *
200: * @param plugIn the {@link PlugIn} to add
201: */
202: public void addPlugIn(final PlugIn plugIn) {
203: synchronized (MetaPlugIn.class) {
204: if (plugIn instanceof CacheLoadedPlugIn)
205: cacheLoadedPlugIns.add((CacheLoadedPlugIn) plugIn);
206:
207: if (plugIn instanceof FeedConfigItemPlugIn)
208: feedConfigItemPlugIns
209: .add((FeedConfigItemPlugIn) plugIn);
210:
211: if (plugIn instanceof MainConfigItemPlugIn)
212: mainConfigItemPlugIns
213: .add((MainConfigItemPlugIn) plugIn);
214:
215: if (plugIn instanceof OutputHandlerConfigItemPlugIn)
216: outputHandlerConfigItemPlugIns
217: .add((OutputHandlerConfigItemPlugIn) plugIn);
218:
219: if (plugIn instanceof PostConfigPlugIn)
220: postConfigPlugIns.add((PostConfigPlugIn) plugIn);
221:
222: if (plugIn instanceof PostFeedDownloadPlugIn)
223: postFeedDownloadPlugIns
224: .add((PostFeedDownloadPlugIn) plugIn);
225:
226: if (plugIn instanceof PostFeedOutputPlugIn)
227: postFeedOutputPlugIns
228: .add((PostFeedOutputPlugIn) plugIn);
229:
230: if (plugIn instanceof PostFeedParsePlugIn)
231: postFeedParsePlugIns.add((PostFeedParsePlugIn) plugIn);
232:
233: if (plugIn instanceof PostOutputHandlerFlushPlugIn)
234: postOutputHandlerFlushPlugIns
235: .add((PostOutputHandlerFlushPlugIn) plugIn);
236:
237: if (plugIn instanceof PreCacheSavePlugIn)
238: preCacheSavePlugIns.add((PreCacheSavePlugIn) plugIn);
239:
240: if (plugIn instanceof ForceFeedDownloadPlugIn)
241: forceFeedDownloadPlugIns
242: .add((ForceFeedDownloadPlugIn) plugIn);
243:
244: if (plugIn instanceof PreFeedDownloadPlugIn)
245: preFeedDownloadPlugIns
246: .add((PreFeedDownloadPlugIn) plugIn);
247:
248: if (plugIn instanceof PreFeedOutputPlugIn)
249: preFeedOutputPlugIns.add((PreFeedOutputPlugIn) plugIn);
250:
251: if (plugIn instanceof PostOutputPlugIn)
252: postOutputPlugIns.add((PostOutputPlugIn) plugIn);
253:
254: if (plugIn instanceof ShutdownPlugIn)
255: shutdownPlugIns.add((ShutdownPlugIn) plugIn);
256:
257: if (plugIn instanceof StartupPlugIn)
258: startupPlugIns.add((StartupPlugIn) plugIn);
259:
260: if (plugIn instanceof UnknownSectionConfigItemPlugIn)
261: unknownSectionConfigItemPlugIns
262: .add((UnknownSectionConfigItemPlugIn) plugIn);
263:
264: allPlugIns.add(plugIn);
265: }
266: }
267:
268: /**
269: * Find all plug-ins that implement the {@link PersistentDataClient}
270: * interface and register them with the specified {@link DataPersister}.
271: *
272: * @param dataPersister the <tt>DataPersister</tt>
273: */
274: public void registerPersistentDataClientPlugIns(
275: DataPersister dataPersister) {
276: for (PlugIn plugIn : allPlugIns) {
277: if (plugIn instanceof PersistentDataClient) {
278: log.debug(plugIn.getPlugInName() + " plug-in is a "
279: + "persistent data client. Registering it.");
280: dataPersister
281: .addPersistentDataClient((PersistentDataClient) plugIn);
282: }
283: }
284: }
285:
286: /*----------------------------------------------------------------------*\
287: Public Methods Required by PlugIn Interface
288: \*----------------------------------------------------------------------*/
289:
290: public String getPlugInName() {
291: return getClass().getName();
292: }
293:
294: public String getPlugInSortKey() {
295: return null; // "invisible" plug-in
296: }
297:
298: public void initPlugIn() throws CurnException {
299: for (PlugIn plugIn : allPlugIns)
300: plugIn.initPlugIn();
301: }
302:
303: public synchronized void runStartupPlugIn() throws CurnException {
304: for (StartupPlugIn plugIn : startupPlugIns) {
305: logPlugInInvocation("runStartupPlugIn", plugIn);
306: plugIn.runStartupPlugIn();
307: }
308: }
309:
310: public synchronized void runMainConfigItemPlugIn(
311: final String sectionName, final String paramName,
312: final CurnConfig config) throws CurnException {
313: for (MainConfigItemPlugIn plugIn : mainConfigItemPlugIns) {
314: logPlugInInvocation("runMainConfigItemPlugIn", plugIn,
315: sectionName, paramName);
316: plugIn.runMainConfigItemPlugIn(sectionName, paramName,
317: config);
318: }
319: }
320:
321: public synchronized boolean runFeedConfigItemPlugIn(
322: final String sectionName, final String paramName,
323: final CurnConfig config, final FeedInfo feedInfo)
324: throws CurnException {
325: boolean keepGoing = true;
326:
327: for (FeedConfigItemPlugIn plugIn : feedConfigItemPlugIns) {
328: logPlugInInvocation("runFeedConfigItemPlugIn", plugIn,
329: sectionName, paramName);
330: keepGoing = plugIn.runFeedConfigItemPlugIn(sectionName,
331: paramName, config, feedInfo);
332: if (!keepGoing) {
333: log.info("Plug-in " + plugIn.getPlugInName()
334: + " has aborted processing of feed "
335: + sectionName);
336: break;
337: }
338: }
339:
340: return keepGoing;
341: }
342:
343: public synchronized boolean runOutputHandlerConfigItemPlugIn(
344: final String sectionName, final String paramName,
345: final CurnConfig config,
346: final ConfiguredOutputHandler handler) throws CurnException {
347: boolean keepGoing = true;
348:
349: for (OutputHandlerConfigItemPlugIn plugIn : outputHandlerConfigItemPlugIns) {
350: logPlugInInvocation("runOutputHandlerConfigItemPlugIn",
351: plugIn, sectionName, paramName);
352: keepGoing = plugIn.runOutputHandlerConfigItemPlugIn(
353: sectionName, paramName, config, handler);
354: if (!keepGoing)
355: break;
356: }
357:
358: return keepGoing;
359: }
360:
361: public synchronized void runUnknownSectionConfigItemPlugIn(
362: final String sectionName, final String paramName,
363: final CurnConfig config) throws CurnException {
364: for (UnknownSectionConfigItemPlugIn plugIn : unknownSectionConfigItemPlugIns) {
365: logPlugInInvocation("runUnknownSectionConfigItemPlugIn",
366: plugIn, sectionName, paramName);
367: plugIn.runUnknownSectionConfigItemPlugIn(sectionName,
368: paramName, config);
369: }
370: }
371:
372: public synchronized void runPostConfigPlugIn(final CurnConfig config)
373: throws CurnException {
374: for (PostConfigPlugIn plugIn : postConfigPlugIns) {
375: logPlugInInvocation("runPostConfigPlugIn", plugIn);
376: plugIn.runPostConfigPlugIn(config);
377: }
378: }
379:
380: public synchronized void runCacheLoadedPlugIn(final FeedCache cache)
381: throws CurnException {
382: for (CacheLoadedPlugIn plugIn : cacheLoadedPlugIns) {
383: logPlugInInvocation("runCacheLoadedPlugIn", plugIn);
384: plugIn.runCacheLoadedPlugIn(cache);
385: }
386: }
387:
388: public synchronized boolean forceFeedDownload(
389: final FeedInfo feedInfo, final FeedCache feedCache)
390: throws CurnException {
391: boolean forceDownload = false;
392:
393: for (ForceFeedDownloadPlugIn plugIn : forceFeedDownloadPlugIns) {
394: logPlugInInvocation("forceFeedDownload", plugIn);
395: forceDownload = plugIn.forceFeedDownload(feedInfo,
396: feedCache);
397:
398: if (forceDownload)
399: break;
400: }
401:
402: return forceDownload;
403: }
404:
405: public synchronized boolean runPreFeedDownloadPlugIn(
406: final FeedInfo feedInfo, final URLConnection urlConn)
407: throws CurnException {
408: boolean keepGoing = true;
409:
410: for (PreFeedDownloadPlugIn plugIn : preFeedDownloadPlugIns) {
411: logPlugInInvocation("runPreFeedDownloadPlugIn", plugIn);
412: keepGoing = plugIn.runPreFeedDownloadPlugIn(feedInfo,
413: urlConn);
414:
415: if (!keepGoing)
416: break;
417: }
418:
419: return keepGoing;
420: }
421:
422: public synchronized boolean runPostFeedDownloadPlugIn(
423: final FeedInfo feedInfo, final File feedDataFile,
424: final String encoding) throws CurnException {
425: boolean keepGoing = true;
426:
427: for (PostFeedDownloadPlugIn plugIn : postFeedDownloadPlugIns) {
428: logPlugInInvocation("runPostFeedDownloadPlugIn", plugIn);
429: keepGoing = plugIn.runPostFeedDownloadPlugIn(feedInfo,
430: feedDataFile, encoding);
431: if (!keepGoing)
432: break;
433: }
434:
435: return keepGoing;
436: }
437:
438: public synchronized boolean runPostFeedParsePlugIn(
439: final FeedInfo feedInfo, final FeedCache feedCache,
440: final RSSChannel channel) throws CurnException {
441: boolean keepGoing = true;
442:
443: for (PostFeedParsePlugIn plugIn : postFeedParsePlugIns) {
444: logPlugInInvocation("runPostFeedParsePlugIn", plugIn);
445: keepGoing = plugIn.runPostFeedParsePlugIn(feedInfo,
446: feedCache, channel);
447: if (!keepGoing)
448: break;
449: }
450:
451: return keepGoing;
452: }
453:
454: public synchronized void runPreFeedOutputPlugIn(
455: final FeedInfo feedInfo, final RSSChannel channel,
456: final OutputHandler outputHandler) throws CurnException {
457: for (PreFeedOutputPlugIn plugIn : preFeedOutputPlugIns) {
458: logPlugInInvocation("runPreFeedOutputPlugIn", plugIn);
459: plugIn.runPreFeedOutputPlugIn(feedInfo, channel,
460: outputHandler);
461: }
462: }
463:
464: public synchronized void runPostFeedOutputPlugIn(
465: final FeedInfo feedInfo, final OutputHandler outputHandler)
466: throws CurnException {
467: for (PostFeedOutputPlugIn plugIn : postFeedOutputPlugIns) {
468: logPlugInInvocation("runPostFeedOutputPlugIn", plugIn);
469: plugIn.runPostFeedOutputPlugIn(feedInfo, outputHandler);
470: }
471: }
472:
473: public synchronized boolean runPostOutputHandlerFlushPlugIn(
474: final OutputHandler outputHandler) throws CurnException {
475: boolean keepGoing = true;
476:
477: for (PostOutputHandlerFlushPlugIn plugIn : postOutputHandlerFlushPlugIns) {
478: logPlugInInvocation("runPostOutputHandlerFlushPlugIn",
479: plugIn);
480: keepGoing = plugIn
481: .runPostOutputHandlerFlushPlugIn(outputHandler);
482:
483: if (!keepGoing)
484: break;
485: }
486:
487: return keepGoing;
488: }
489:
490: public void runPostOutputPlugIn(
491: final Collection<OutputHandler> outputHandlers)
492: throws CurnException {
493: for (PostOutputPlugIn plugIn : postOutputPlugIns) {
494: logPlugInInvocation("runPostOutputPlugIn", plugIn);
495: plugIn.runPostOutputPlugIn(outputHandlers);
496: }
497: }
498:
499: public synchronized void runPreCacheSavePlugIn(final FeedCache cache)
500: throws CurnException {
501: for (PreCacheSavePlugIn plugIn : preCacheSavePlugIns) {
502: logPlugInInvocation("runPreCacheSavePlugIn", plugIn);
503: plugIn.runPreCacheSavePlugIn(cache);
504: }
505: }
506:
507: public synchronized void runShutdownPlugIn() throws CurnException {
508: for (ShutdownPlugIn plugIn : shutdownPlugIns) {
509: logPlugInInvocation("runShutdownPlugIn", plugIn);
510: plugIn.runShutdownPlugIn();
511: }
512: }
513:
514: /*----------------------------------------------------------------------*\
515: Package-visible Methods
516: \*----------------------------------------------------------------------*/
517:
518: /**
519: * Create the MetaPlugIn
520: *
521: * @return the created MetaPlugIn
522: *
523: * @throws CurnException on error
524: */
525: static MetaPlugIn createMetaPlugIn() throws CurnException {
526: assert (metaPlugInWrapper == null);
527: try {
528: metaPlugInWrapper = new MetaPlugIn();
529: return metaPlugInWrapper;
530: }
531:
532: catch (Exception ex) {
533: throw new CurnException("Can't allocate MetaPlugIn", ex);
534: }
535: }
536:
537: /*----------------------------------------------------------------------*\
538: Private Methods
539: \*----------------------------------------------------------------------*/
540:
541: /**
542: * Log a plug-in invocation.
543: *
544: * @param methodName calling method name
545: * @param plugIn plug-in class
546: * @param args method args, if any
547: */
548: private void logPlugInInvocation(final String methodName,
549: final PlugIn plugIn, final Object... args) // NOPMD
550: {
551: if (log.isDebugEnabled()) {
552: StringBuilder buf = new StringBuilder();
553:
554: buf.append("invoking ");
555: buf.append(methodName);
556: String sep = "(";
557:
558: for (Object arg : args) {
559: buf.append(sep);
560: sep = ", ";
561:
562: if (arg instanceof Number)
563: buf.append(arg.toString());
564:
565: else {
566: buf.append('"');
567: buf.append(arg.toString());
568: buf.append('"');
569: }
570: }
571:
572: buf.append(") for plug-in ");
573: buf.append(plugIn.getClass().getName());
574: log.debug(buf.toString());
575: }
576: }
577: }
|