Source Code Cross Referenced for XMLDataPersister.java in  » RSS-RDF » curn » org » clapper » curn » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » RSS RDF » curn » org.clapper.curn 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*---------------------------------------------------------------------------*\
002:         $Id: XMLDataPersister.java 6962 2007-08-12 04:54:23Z bmc $
003:        \*---------------------------------------------------------------------------*/
004:
005:        package org.clapper.curn;
006:
007:        import java.io.File;
008:        import java.io.IOException;
009:        import java.io.Writer;
010:        import java.net.MalformedURLException;
011:        import java.net.URL;
012:        import java.util.ArrayList;
013:        import java.util.Collection;
014:        import java.util.Date;
015:        import java.util.HashMap;
016:        import java.util.Iterator;
017:        import java.util.List;
018:        import java.util.Map;
019:
020:        import org.clapper.util.config.ConfigurationException;
021:        import org.clapper.util.io.IOExceptionExt;
022:        import org.clapper.util.logging.Logger;
023:        import org.clapper.util.text.TextUtil;
024:        import org.jdom.Document;
025:        import org.jdom.Element;
026:        import org.jdom.input.SAXBuilder;
027:        import org.jdom.output.Format;
028:        import org.jdom.output.XMLOutputter;
029:
030:        /**
031:         * @version <tt>$Revision: 6962 $</tt>
032:         */
033:        public class XMLDataPersister extends DataPersister {
034:            /*----------------------------------------------------------------------*\
035:                                       Private Constants
036:            \*----------------------------------------------------------------------*/
037:
038:            private static final int DEF_TOTAL_CACHE_BACKUPS = 0;
039:
040:            private static final String VAR_CACHE_FILE = "CacheFile";
041:            private static final String VAR_TOTAL_CACHE_BACKUPS = "TotalCacheBackups";
042:
043:            /**
044:             * Constants for the old XML format
045:             */
046:            private static final String OLD_XML_ROOT_ELEMENT = "curn_cache";
047:            private static final String OLD_XML_ENTRY_ELEMENT = "cache_entry";
048:            private static final String OLD_XML_ENTRY_TIMESTAMP_ATTR = "timestamp";
049:            private static final String OLD_XML_ENTRY_CHANNEL_URL_ATTR = "channel_URL";
050:            private static final String OLD_XML_ENTRY_ENTRY_URL_ATTR = "entry_URL";
051:            private static final String OLD_XML_ENTRY_ENTRY_ID_ATTR = "entry_ID";
052:            private static final String OLD_XML_ENTRY_PUB_DATE_ATTR = "pub_date";
053:
054:            /**
055:             * Constants for the new XML format.
056:             */
057:            private static final String XML_ROOT_ELEMENT = "curn-data";
058:            private static final String XML_FEED_ELEMENT = "feed";
059:            private static final String XML_ITEM_ELEMENT = "item";
060:            private static final String XML_ITEM_METADATA_ELEMENT = "item-metadata";
061:            private static final String XML_FEED_METADATA_ELEMENT = "feed-metadata";
062:            private static final String XML_EXTRA_METADATA_ELEMENT = "extra-metadata";
063:            private static final String XML_METADATA_NAMESPACE_ATTR = "namespace";
064:            private static final String XML_METADATUM_ELEMENT = "metadatum";
065:            private static final String XML_METADATUM_NAME_ATTR = "name";
066:            private static final String XML_METADATUM_VALUE_ATTR = "value";
067:            private static final String XML_TIMESTAMP_ATTR = "timestamp";
068:            private static final String XML_URL_ATTR = "url";
069:            private static final String XML_ID_ATTR = "id";
070:            private static final String XML_PUB_DATE_ATTR = "pub-date";
071:
072:            /*----------------------------------------------------------------------*\
073:                                     Private Instance Data
074:            \*----------------------------------------------------------------------*/
075:
076:            private int totalCacheBackups = DEF_TOTAL_CACHE_BACKUPS;
077:            private File metadataFile = null;
078:
079:            /**
080:             * Root XML element, used while saving.
081:             */
082:            private Element rootElementForSaving = null;
083:
084:            /**
085:             * For logging
086:             */
087:            private static final Logger log = new Logger(XMLDataPersister.class);
088:
089:            /*----------------------------------------------------------------------*\
090:                                            Constructor
091:             \*----------------------------------------------------------------------*/
092:
093:            /**
094:             * Creates a new instance of XMLDataPersister
095:             */
096:            public XMLDataPersister() {
097:            }
098:
099:            /*----------------------------------------------------------------------*\
100:                                        Public Methods
101:            \*----------------------------------------------------------------------*/
102:
103:            /**
104:             * Called when the <tt>DataPersister</tt> is first instantiated. Useful
105:             * for retrieving configuration values, etc.
106:             *
107:             * @param curnConfig  the configuration
108:             * @throws CurnException on error
109:             */
110:            public void init(CurnConfig curnConfig) throws CurnException {
111:                try {
112:                    String cacheFileName = curnConfig.getOptionalStringValue(
113:                            CurnConfig.MAIN_SECTION, VAR_CACHE_FILE, null);
114:                    if (cacheFileName != null) {
115:                        metadataFile = CurnUtil
116:                                .mapConfiguredPathName(cacheFileName);
117:                        if (metadataFile.isDirectory()) {
118:                            throw new CurnException(
119:                                    Constants.BUNDLE_NAME,
120:                                    "XMLDataPersister.cacheIsDir",
121:                                    "Configured XML cache file \"{0}\" is a directory.",
122:                                    new Object[] { metadataFile.getPath() });
123:                        }
124:                    }
125:
126:                    totalCacheBackups = curnConfig.getOptionalCardinalValue(
127:                            CurnConfig.MAIN_SECTION, VAR_TOTAL_CACHE_BACKUPS,
128:                            DEF_TOTAL_CACHE_BACKUPS);
129:                }
130:
131:                catch (ConfigurationException ex) {
132:                    throw new CurnException(ex);
133:                }
134:            }
135:
136:            /*----------------------------------------------------------------------*\
137:                                       Protected Methods
138:            \*----------------------------------------------------------------------*/
139:
140:            /**
141:             * Determine whether the data persister subclass is enabled or not (i.e.,
142:             * whether or not metadata is to be loaded and saved). The configuration
143:             * usually determines whether or not the data persister is enabled.
144:             *
145:             * @return <tt>true</tt> if enabled, <tt>false</tt> if disabled.
146:             */
147:            protected boolean isEnabled() {
148:                return metadataFile != null;
149:            }
150:
151:            /**
152:             * Called at the beginning of the actual save operation to initialize
153:             * the save, etc.
154:             *
155:             * @throws CurnException on error
156:             */
157:            protected void startSaveOperation() throws CurnException {
158:                assert (isEnabled());
159:
160:                log.debug("Saving feed metadata to \"" + metadataFile.getPath()
161:                        + "\". Total backups=" + totalCacheBackups);
162:
163:                // Create the DOM's root element.
164:
165:                rootElementForSaving = new Element(XML_ROOT_ELEMENT);
166:                rootElementForSaving.setAttribute(XML_TIMESTAMP_ATTR, String
167:                        .valueOf(System.currentTimeMillis()));
168:            }
169:
170:            /**
171:             * Called at the end of the actual save operation to flush files, clean
172:             * up, etc.
173:             *
174:             * @throws CurnException on error
175:             */
176:            protected void endSaveOperation() throws CurnException {
177:                try {
178:                    Document document = new Document(rootElementForSaving);
179:                    Format outputFormat = Format.getPrettyFormat();
180:                    outputFormat.setLineSeparator(System
181:                            .getProperty("line.separator"));
182:                    XMLOutputter xmlOut = new XMLOutputter(outputFormat);
183:
184:                    // Open the cache file. For the cache file, the index
185:                    // marker goes at the end of the file (since the extension
186:                    // doesn't matter as much). This allows the file names to
187:                    // sort better in a directory listing.
188:
189:                    Writer cacheOut = CurnUtil.openOutputFile(metadataFile,
190:                            null, CurnUtil.IndexMarker.AFTER_EXTENSION,
191:                            totalCacheBackups);
192:
193:                    //xmlOut.output(document, new XMLWriter(cacheOut));
194:                    xmlOut.output(document, cacheOut);
195:                }
196:
197:                catch (IOException ex) {
198:                    throw new CurnException("Failed to write XML cache file \""
199:                            + metadataFile.getPath() + "\"", ex);
200:                }
201:
202:                catch (IOExceptionExt ex) {
203:                    throw new CurnException("Failed to write XML cache file \""
204:                            + metadataFile.getPath() + "\"", ex);
205:                }
206:            }
207:
208:            /**
209:             * Save the data for one feed, including the items.
210:             *
211:             * @param feedData  the feed data to be saved
212:             *
213:             * @throws CurnException on error
214:             */
215:            protected void saveFeedData(PersistentFeedData feedData)
216:                    throws CurnException {
217:                FeedCacheEntry feedCacheData = feedData.getFeedCacheEntry();
218:                URL channelURL = feedCacheData.getChannelURL();
219:
220:                Element channelElement = new Element(XML_FEED_ELEMENT);
221:                channelElement
222:                        .setAttribute(XML_URL_ATTR, channelURL.toString());
223:                channelElement.setAttribute(XML_TIMESTAMP_ATTR, String
224:                        .valueOf(feedCacheData.getTimestamp()));
225:                channelElement.setAttribute(XML_ID_ATTR, feedCacheData
226:                        .getUniqueID());
227:                rootElementForSaving.addContent(channelElement);
228:
229:                // Now the feed metadata
230:
231:                fillInMetadata(feedData.getFeedMetadata(),
232:                        XML_FEED_METADATA_ELEMENT, channelElement);
233:
234:                // Okay, the feed element has been built. Time to add the items to it.
235:
236:                for (PersistentFeedItemData itemData : feedData
237:                        .getPersistentFeedItems()) {
238:                    Element itemElement = new Element(XML_ITEM_ELEMENT);
239:                    channelElement.addContent(itemElement);
240:
241:                    FeedCacheEntry itemCacheData = itemData.getFeedCacheEntry();
242:                    itemElement.setAttribute(XML_TIMESTAMP_ATTR, String
243:                            .valueOf(itemCacheData.getTimestamp()));
244:                    itemElement.setAttribute(XML_ID_ATTR, itemCacheData
245:                            .getUniqueID());
246:                    itemElement.setAttribute(XML_URL_ATTR, itemCacheData
247:                            .getEntryURL().toString());
248:
249:                    // Only write the publication date if it's present.
250:
251:                    Date pubDate = itemCacheData.getPublicationDate();
252:                    if (pubDate != null) {
253:                        itemElement.setAttribute(XML_PUB_DATE_ATTR, String
254:                                .valueOf(pubDate.getTime()));
255:                    }
256:
257:                    // Fill in the metadata for the item.
258:
259:                    fillInMetadata(itemData.getItemMetadata(),
260:                            XML_ITEM_METADATA_ELEMENT, itemElement);
261:                }
262:            }
263:
264:            /**
265:             * Save any extra metadata (i.e., metadata that isn't attached to a
266:             * specific feed or a specific item).
267:             *
268:             * @param metadata the collection of metadata items
269:             *
270:             * @throws CurnException on error
271:             */
272:            protected void saveExtraMetadata(
273:                    Collection<PersistentMetadataGroup> metadata)
274:                    throws CurnException {
275:                fillInMetadata(metadata, XML_EXTRA_METADATA_ELEMENT,
276:                        rootElementForSaving);
277:            }
278:
279:            /**
280:             * Called at the beginning of the load operation to initialize
281:             * the load.
282:             *
283:             * @throws CurnException on error
284:             */
285:            protected void startLoadOperation() throws CurnException {
286:                assert (isEnabled());
287:                log.debug("Starting load of XML curn data.");
288:            }
289:
290:            /**
291:             * Called at the end of the load operation to close files, clean
292:             * up, etc.
293:             *
294:             * @throws CurnException on error
295:             */
296:            protected void endLoadOperation() throws CurnException {
297:                log.debug("Load of XML curn data complete.");
298:            }
299:
300:            /**
301:             * The actual load method; only called if the object is enabled.
302:             *
303:             * @param loadedDataHandler object to receive data as it's loaded
304:             *
305:             * @throws CurnException on error
306:             */
307:            protected void doLoad(LoadedDataHandler loadedDataHandler)
308:                    throws CurnException {
309:                String filePath = metadataFile.getPath();
310:                if (metadataFile.exists()) {
311:                    log
312:                            .debug("Reading feed metadata from \"" + filePath
313:                                    + "\"");
314:
315:                    // First, parse the XML file into a DOM.
316:
317:                    SAXBuilder builder = new SAXBuilder();
318:                    Document document;
319:
320:                    log
321:                            .info("Attempting to parse \"" + filePath
322:                                    + "\" as XML.");
323:                    try {
324:                        document = builder.build(metadataFile);
325:                    }
326:
327:                    catch (Throwable ex) {
328:                        log.error(ex);
329:                        throw new CurnException(ex);
330:                    }
331:
332:                    log.debug("XML parse succeeded.");
333:
334:                    // Get the top-level element and verify that it's the one
335:                    // we want.
336:
337:                    Element root = document.getRootElement();
338:                    String rootTagName = root.getName();
339:
340:                    if (rootTagName.equals(OLD_XML_ROOT_ELEMENT)) {
341:                        log.debug("Reading old-style <" + OLD_XML_ROOT_ELEMENT
342:                                + "> cache file.");
343:                        readOldXMLCache(document, filePath, loadedDataHandler);
344:                    }
345:
346:                    else if (rootTagName.equals(XML_ROOT_ELEMENT)) {
347:                        log.debug("Reading new-style <" + XML_ROOT_ELEMENT
348:                                + "> metadata file.");
349:                        readNewXMLMetaData(document, loadedDataHandler);
350:                    }
351:
352:                    else {
353:                        throw new CurnException(
354:                                Constants.BUNDLE_NAME,
355:                                "XMLDataPersister.nonCacheXML",
356:                                "File \"{0}\" is not a curn XML metadata file. The root "
357:                                        + "XML element is <{1}>, not the expected <{2}> or <{3}>",
358:                                new Object[] { filePath, rootTagName,
359:                                        OLD_XML_ROOT_ELEMENT, XML_ROOT_ELEMENT });
360:                    }
361:                }
362:            }
363:
364:            /*----------------------------------------------------------------------*\
365:                                        Private Methods
366:            \*----------------------------------------------------------------------*/
367:
368:            private void fillInMetadata(
369:                    Collection<PersistentMetadataGroup> metadata,
370:                    String elementName, Element parentElement) {
371:                for (PersistentMetadataGroup metadataGroup : metadata) {
372:                    Element metadataElement = new Element(elementName);
373:                    metadataElement.setAttribute(XML_METADATA_NAMESPACE_ATTR,
374:                            metadataGroup.getNamespace());
375:                    parentElement.addContent(metadataElement);
376:
377:                    Map<String, String> nameValuePairs = metadataGroup
378:                            .getMetadata();
379:                    for (Map.Entry<String, String> nameValuePair : nameValuePairs
380:                            .entrySet()) {
381:                        Element metadatumElement = new Element(
382:                                XML_METADATUM_ELEMENT);
383:                        metadatumElement.setAttribute(XML_METADATUM_NAME_ATTR,
384:                                nameValuePair.getKey());
385:                        metadatumElement.setAttribute(XML_METADATUM_VALUE_ATTR,
386:                                nameValuePair.getValue());
387:                        metadataElement.addContent(metadatumElement);
388:                    }
389:                }
390:            }
391:
392:            /**
393:             * Attempt to parse an old-style XML cache. This method will go away
394:             * soon.
395:             *
396:             * @param document          the parsed XML document
397:             * @param filePath          the path to the file, for errors
398:             * @param loadedDataHandler the callback to invoke with loaded data
399:             *
400:             * @throws CurnException on error
401:             */
402:            private void readOldXMLCache(final Document document,
403:                    final String filePath,
404:                    final LoadedDataHandler loadedDataHandler)
405:                    throws CurnException {
406:                // Get the top-level element and verify that it's the one
407:                // we want.
408:
409:                Element root = document.getRootElement();
410:                String rootTagName = root.getName();
411:                assert (rootTagName.equals(OLD_XML_ROOT_ELEMENT));
412:
413:                // Okay, it's a curn cache. Start traversing the child nodes,
414:                // parsing each cache entry.
415:
416:                Map<URL, PersistentFeedData> loadedData = new HashMap<URL, PersistentFeedData>();
417:
418:                List<?> childNodes = root.getChildren();
419:                for (Iterator<?> it = childNodes.iterator(); it.hasNext();) {
420:                    Element childNode = (Element) it.next();
421:
422:                    // Skip non-element nodes (like text).
423:
424:                    String nodeName = childNode.getName();
425:                    if (!nodeName.equals(OLD_XML_ENTRY_ELEMENT)) {
426:                        log.warn("Skipping unexpected XML element <" + nodeName
427:                                + "> in curn XML cache file \"" + filePath
428:                                + "\".");
429:                        continue;
430:                    }
431:
432:                    try {
433:                        FeedCacheEntry entry = parseOldXMLCacheEntry(childNode);
434:                        URL feedURL = entry.getChannelURL();
435:                        PersistentFeedData feedData = loadedData.get(feedURL);
436:                        log.debug("readOldXMLCache: read entry "
437:                                + entry.getEntryURL());
438:                        if (feedData == null) {
439:                            feedData = new PersistentFeedData();
440:                            loadedData.put(feedURL, feedData);
441:                        }
442:
443:                        if (entry.isChannelEntry()) {
444:                            feedData.setFeedCacheEntry(entry);
445:                        }
446:
447:                        else {
448:                            feedData
449:                                    .addPersistentFeedItem(new PersistentFeedItemData(
450:                                            entry));
451:                        }
452:                    }
453:
454:                    catch (CurnException ex) {
455:                        // Bad entry. Log the error, but move on.
456:
457:                        log.error("Error parsing feed cache entry", ex);
458:                    }
459:                }
460:
461:                for (PersistentFeedData feedData : loadedData.values())
462:                    loadedDataHandler.feedLoaded(feedData);
463:            }
464:
465:            /**
466:             * Parse an old-style XML feed cache entry. This method will go away
467:             * soon.
468:             *
469:             * @param element  the XML element for the feed cache entry
470:             *
471:             * @return the FeedCacheEntry
472:             *
473:             * @throws CurnException on error
474:             */
475:            private FeedCacheEntry parseOldXMLCacheEntry(final Element element)
476:                    throws CurnException {
477:                FeedCacheEntry result = null;
478:
479:                // Parse out the attributes.
480:
481:                String entryID = getRequiredXMLAttribute(element,
482:                        OLD_XML_ENTRY_ENTRY_ID_ATTR);
483:                String sChannelURL = getRequiredXMLAttribute(element,
484:                        OLD_XML_ENTRY_CHANNEL_URL_ATTR);
485:                String sEntryURL = getRequiredXMLAttribute(element,
486:                        OLD_XML_ENTRY_ENTRY_URL_ATTR);
487:                String sTimestamp = getRequiredXMLAttribute(element,
488:                        OLD_XML_ENTRY_TIMESTAMP_ATTR);
489:                String sPubDate = getOptionalXMLAttribute(element,
490:                        OLD_XML_ENTRY_PUB_DATE_ATTR, null);
491:
492:                if ((entryID != null) && (sChannelURL != null)
493:                        && (sEntryURL != null) && (sTimestamp != null)) {
494:                    // Parse the timestamp.
495:
496:                    long timestamp = 0;
497:                    try {
498:                        timestamp = Long.parseLong(sTimestamp);
499:                    }
500:
501:                    catch (NumberFormatException ex) {
502:                        throw new CurnException("Bad timestamp value of \""
503:                                + sTimestamp + "\" for <"
504:                                + OLD_XML_ENTRY_ELEMENT + "> with unique ID \""
505:                                + entryID + "\". Skipping entry.");
506:                    }
507:
508:                    // Parse the publication date, if any
509:
510:                    Date publicationDate = null;
511:                    if (sPubDate != null) {
512:                        try {
513:                            publicationDate = new Date(Long.parseLong(sPubDate));
514:                        }
515:
516:                        catch (NumberFormatException ex) {
517:                            log.error("Bad publication date value of \""
518:                                    + sPubDate + "\" for <"
519:                                    + OLD_XML_ENTRY_ELEMENT
520:                                    + "> with unique ID \"" + entryID
521:                                    + "\". Ignoring publication date.");
522:                        }
523:                    }
524:
525:                    // Parse the URLs.
526:
527:                    URL channelURL = null;
528:                    try {
529:                        channelURL = new URL(sChannelURL);
530:                    }
531:
532:                    catch (MalformedURLException ex) {
533:                        throw new CurnException("Bad channel URL \""
534:                                + sChannelURL + "\" for <"
535:                                + OLD_XML_ENTRY_ELEMENT + "> with unique ID \""
536:                                + entryID + "\". Skipping entry.");
537:                    }
538:
539:                    URL entryURL = null;
540:                    try {
541:                        entryURL = new URL(sEntryURL);
542:                    }
543:
544:                    catch (MalformedURLException ex) {
545:                        throw new CurnException("Bad item URL \"" + sChannelURL
546:                                + "\" for <" + OLD_XML_ENTRY_ELEMENT
547:                                + "> with unique ID \"" + entryID
548:                                + "\". Skipping entry.");
549:                    }
550:
551:                    result = new FeedCacheEntry(entryID, channelURL, entryURL,
552:                            publicationDate, timestamp);
553:                }
554:
555:                return result;
556:            }
557:
558:            /**
559:             * Attempt to parse a new-style XML metadata file.
560:             *
561:             * @param document the parsed XML file
562:             * @param loadedDataHandler the callback to invoke with loaded data
563:             *
564:             * @throws CurnException on error
565:             */
566:            private void readNewXMLMetaData(final Document document,
567:                    final LoadedDataHandler loadedDataHandler)
568:                    throws CurnException {
569:                // Get the top-level element and verify that it's the one
570:                // we want.
571:
572:                Element root = document.getRootElement();
573:                String rootTagName = root.getName();
574:                assert (rootTagName.equals(XML_ROOT_ELEMENT));
575:
576:                // Get the list of channels.
577:
578:                List<?> channels = root.getChildren(XML_FEED_ELEMENT);
579:                for (Iterator<?> itChannel = channels.iterator(); itChannel
580:                        .hasNext();) {
581:                    // Parse the channel element itself.
582:
583:                    Element channelElement = (Element) itChannel.next();
584:                    FeedCacheEntry entry = parseXMLFeedElement(channelElement);
585:                    PersistentFeedData feedData = new PersistentFeedData(entry);
586:                    URL channelURL = entry.getChannelURL();
587:
588:                    // Parse any metadata in the channel.
589:
590:                    List<?> feedMetadata = channelElement
591:                            .getChildren(XML_FEED_METADATA_ELEMENT);
592:                    feedData.addFeedMetadata(parseMetadata(feedMetadata));
593:
594:                    // Get the list of items and process each one.
595:
596:                    List<?> items = channelElement
597:                            .getChildren(XML_ITEM_ELEMENT);
598:                    for (Iterator<?> itItem = items.iterator(); itItem
599:                            .hasNext();) {
600:                        // Parse the item element itself.
601:
602:                        Element itemElement = (Element) itItem.next();
603:                        entry = parseXMLItemElement(itemElement, channelURL);
604:                        PersistentFeedItemData itemData = new PersistentFeedItemData(
605:                                entry);
606:                        feedData.addPersistentFeedItem(itemData);
607:
608:                        // Get and process the item metadata
609:
610:                        List<?> itemMetadata = itemElement
611:                                .getChildren(XML_ITEM_METADATA_ELEMENT);
612:                        itemData.addItemMetadata(parseMetadata(itemMetadata));
613:                    }
614:
615:                    loadedDataHandler.feedLoaded(feedData);
616:                }
617:
618:                // Finally, parse any extra metadata.
619:
620:                Collection<PersistentMetadataGroup> extraMetadata = parseMetadata(root
621:                        .getChildren(XML_EXTRA_METADATA_ELEMENT));
622:                for (PersistentMetadataGroup metadataGroup : extraMetadata)
623:                    loadedDataHandler.extraMetadataLoaded(metadataGroup);
624:            }
625:
626:            private Collection<PersistentMetadataGroup> parseMetadata(
627:                    List<?> metadataElements) {
628:                Collection<PersistentMetadataGroup> result = new ArrayList<PersistentMetadataGroup>();
629:
630:                for (Iterator<?> it = metadataElements.iterator(); it.hasNext();) {
631:                    Element mdElement = (Element) it.next();
632:                    String namespace = getRequiredXMLAttribute(mdElement,
633:                            XML_METADATA_NAMESPACE_ATTR);
634:                    List<?> nameValuePairs = mdElement
635:                            .getChildren(XML_METADATUM_ELEMENT);
636:                    PersistentMetadataGroup metadataGroup = new PersistentMetadataGroup(
637:                            namespace);
638:
639:                    for (Iterator<?> itNV = nameValuePairs.iterator(); itNV
640:                            .hasNext();) {
641:                        Element nvElement = (Element) itNV.next();
642:                        String name = getRequiredXMLAttribute(nvElement,
643:                                XML_METADATUM_NAME_ATTR);
644:                        String value = getRequiredXMLAttribute(nvElement,
645:                                XML_METADATUM_VALUE_ATTR);
646:                        metadataGroup.addMetadataItem(name, value);
647:                    }
648:
649:                    result.add(metadataGroup);
650:                }
651:
652:                return result;
653:            }
654:
655:            /**
656:             * Parse an XML feed metadata channel element. This method only parses the
657:             * attributes of the channel element; it does not handle any child
658:             * elements.
659:             *
660:             * @param channelElement the XML element for the channel
661:             *
662:             * @return the FeedCacheEntry object for the feed
663:             *
664:             * @throws CurnException on error
665:             */
666:            private FeedCacheEntry parseXMLFeedElement(
667:                    final Element channelElement) throws CurnException {
668:                // Parse the channel and create an entry for it.
669:
670:                String sChannelURL = getRequiredXMLAttribute(channelElement,
671:                        XML_URL_ATTR);
672:                String id = getRequiredXMLAttribute(channelElement, XML_ID_ATTR);
673:                String sTimestamp = getRequiredXMLAttribute(channelElement,
674:                        XML_TIMESTAMP_ATTR);
675:
676:                long timestamp = 0;
677:                try {
678:                    timestamp = Long.parseLong(sTimestamp);
679:                }
680:
681:                catch (NumberFormatException ex) {
682:                    throw new CurnException("Bad timestamp value of \""
683:                            + sTimestamp + "\" for <" + XML_FEED_ELEMENT
684:                            + "> with unique ID \"" + id
685:                            + "\". Skipping entry.");
686:                }
687:
688:                URL channelURL;
689:                try {
690:                    channelURL = new URL(sChannelURL);
691:                }
692:
693:                catch (MalformedURLException ex) {
694:                    throw new CurnException("Bad channel URL \"" + sChannelURL
695:                            + "\" for <" + XML_FEED_ELEMENT
696:                            + "> with unique ID \"" + id + "\"");
697:                }
698:
699:                FeedCacheEntry entry = new FeedCacheEntry(id, channelURL,
700:                        channelURL, null, timestamp);
701:
702:                // Now the feed metadata.
703:
704:                return entry;
705:            }
706:
707:            /**
708:             * Parse an XML feed metadata item entry.
709:             *
710:             * @param itemElement  the XML element for the item entry
711:             * @param channelURL   the URL of the parent channel
712:             *
713:             * @return the FeedCacheEntry
714:             *
715:             * @throws CurnException on error
716:             */
717:            private FeedCacheEntry parseXMLItemElement(
718:                    final Element itemElement, final URL channelURL)
719:                    throws CurnException {
720:                FeedCacheEntry result = null;
721:
722:                // Parse out the attributes.
723:
724:                String id = getRequiredXMLAttribute(itemElement, XML_ID_ATTR);
725:                String sItemURL = getRequiredXMLAttribute(itemElement,
726:                        XML_URL_ATTR);
727:                String sTimestamp = getRequiredXMLAttribute(itemElement,
728:                        XML_TIMESTAMP_ATTR);
729:                String sPubDate = getOptionalXMLAttribute(itemElement,
730:                        XML_PUB_DATE_ATTR, null);
731:
732:                if ((id != null) && (sItemURL != null) && (sTimestamp != null)) {
733:                    // Parse the timestamp.
734:
735:                    long timestamp = 0;
736:                    try {
737:                        timestamp = Long.parseLong(sTimestamp);
738:                    }
739:
740:                    catch (NumberFormatException ex) {
741:                        throw new CurnException("Bad timestamp value of \""
742:                                + sTimestamp + "\" for <" + XML_ITEM_ELEMENT
743:                                + "> with unique ID \"" + id
744:                                + "\". Skipping entry.");
745:                    }
746:
747:                    // Parse the publication date, if any
748:
749:                    Date publicationDate = null;
750:                    if (sPubDate != null) {
751:                        try {
752:                            long pubTimestamp = Long.parseLong(sPubDate);
753:                            if (pubTimestamp > 0)
754:                                publicationDate = new Date(pubTimestamp);
755:                        }
756:
757:                        catch (NumberFormatException ex) {
758:                            log.error("Bad publication date value of \""
759:                                    + sPubDate + "\" for <" + XML_ITEM_ELEMENT
760:                                    + "> with unique ID \"" + id
761:                                    + "\". Ignoring publication date.");
762:                        }
763:                    }
764:
765:                    // Parse the URL.
766:
767:                    URL itemURL = null;
768:                    try {
769:                        itemURL = new URL(sItemURL);
770:                    }
771:
772:                    catch (MalformedURLException ex) {
773:                        throw new CurnException("Bad item URL \"" + sItemURL
774:                                + "\" for <" + XML_ITEM_ELEMENT
775:                                + "> with unique ID \"" + id
776:                                + "\". Skipping entry.");
777:                    }
778:
779:                    result = new FeedCacheEntry(id, channelURL, itemURL,
780:                            publicationDate, timestamp);
781:                }
782:
783:                return result;
784:            }
785:
786:            /**
787:             * Retrieve an optional XML attribute value from a list of attributes.
788:             * If the attribute is missing or empty, the default is returned.
789:             *
790:             * @param element      the XML element
791:             * @param defaultValue the default value
792:             * @param name         the attribute name
793:             *
794:             * @return the attribute's value, or null if the attribute wasn't found
795:             */
796:            private String getOptionalXMLAttribute(final Element element,
797:                    final String name, final String defaultValue) {
798:                String value = element.getAttributeValue(name);
799:                if ((value != null) && TextUtil.stringIsEmpty(value))
800:                    value = null;
801:
802:                return (value == null) ? defaultValue : value;
803:            }
804:
805:            /**
806:             * Retrieve an XML attribute value from a list of attributes. If the
807:             * attribute is missing, the error is logged (but an exception is not
808:             * thrown).
809:             *
810:             * @param element the element
811:             * @param name    the attribute name
812:             *
813:             * @return the attribute's value, or null if the attribute wasn't found
814:             */
815:            private String getRequiredXMLAttribute(final Element element,
816:                    final String name) {
817:                String value = getOptionalXMLAttribute(element, name, null);
818:
819:                if (value == null) {
820:                    log.error("<" + element.getName()
821:                            + "> is missing required " + "\"" + name
822:                            + "\" XML attribute.");
823:                }
824:
825:                return value;
826:            }
827:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.