Author: tchemit Date: 2008-05-29 21:42:11 +0000 (Thu, 29 May 2008) New Revision: 71 Added: trunk/lutinrss/src/main/java/org/codelutin/rss/RSSGeneratorHelper.java trunk/lutinrss/src/test/java/org/codelutin/rss/RSSGeneratorHelperTest.java Log: introduction class RSSGeneratorHelper pour g?\195?\169n?\195?\169rer des fedd dans un fichier TODO faire les tests sur l'ajout d'entr?\195?\169es dans les feeds Added: trunk/lutinrss/src/main/java/org/codelutin/rss/RSSGeneratorHelper.java =================================================================== --- trunk/lutinrss/src/main/java/org/codelutin/rss/RSSGeneratorHelper.java (rev 0) +++ trunk/lutinrss/src/main/java/org/codelutin/rss/RSSGeneratorHelper.java 2008-05-29 21:42:11 UTC (rev 71) @@ -0,0 +1,276 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.codelutin.rss; + +import com.sun.syndication.feed.synd.SyndContent; +import com.sun.syndication.feed.synd.SyndContentImpl; +import com.sun.syndication.feed.synd.SyndEntry; +import com.sun.syndication.feed.synd.SyndEntryImpl; +import com.sun.syndication.feed.synd.SyndFeed; +import com.sun.syndication.feed.synd.SyndFeedImpl; +import com.sun.syndication.io.FeedException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * helper class to write rss feeds in a file + * @author tony + */ +public class RSSGeneratorHelper { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static final Log log = LogFactory.getLog(RSSGeneratorHelper.class); + /** date formater use to save date in feed and entries */ + static final DateFormat DATE_PARSER = new SimpleDateFormat("dd/MM/yyyy"); + /** shared jvm instance */ + protected static RSSGeneratorHelper instance; + /** dictonnary of field <-> property for feed */ + protected final EnumMap<Field, String> feedProperties; + /** dictonnary of field <-> property for entry */ + protected final EnumMap<Field, String> itemProperties; + + public static RSSGeneratorHelper getInstance() { + if (instance == null) { + instance = new RSSGeneratorHelper(); + } + return instance; + } + + protected RSSGeneratorHelper() { + itemProperties = new EnumMap<Field, String>(Field.class); + feedProperties = new EnumMap<Field, String>(Field.class); + feedProperties.put(Field.NAME, "title"); + feedProperties.put(Field.LINK, "link"); + // cela engendre des NPE... + feedProperties.put(Field.IMAGE, "image.url"); + feedProperties.put(Field.DESCRIPTION, "description"); + feedProperties.put(Field.AUTHOR, "author"); + feedProperties.put(Field.TIME, "publishedDate"); + + itemProperties.put(Field.NAME, "title"); + itemProperties.put(Field.LINK, "link"); + itemProperties.put(Field.IMAGE, null); + itemProperties.put(Field.DESCRIPTION, "description.value"); + itemProperties.put(Field.AUTHOR, "author"); + itemProperties.put(Field.TIME, "publishedDate"); + } + + /** + * + * @param url location where to create the file + * @param values properties of the feed + */ + public void createFeedFile(URL url, FeedType type, Map<Field, Object> values) throws IOException, FeedException, ParseException { + File f = getFile(url); + + if (f.exists()) { + throw new IllegalStateException("file already existing in " + url); + } + + // make sure parent exists + f.getParentFile().mkdirs(); + + // block until can acquire lock + FileLock lock = acquireLock(f); + + try { + + SyndFeed feed = createFeed(type, values); + + RSSIOUtil.saveFeed(f, feed); + + } finally { + releaseLock(f,lock); + } + + } + + /** + * Add a item to an existing feed file. + * + * @param url location of feed to used + * @param nbEntries number of maximum entries to be written in feed file + * @param values dictionnary of properties to write + */ + public void addItemToFeedFile(URL url, int nbEntries, Map<Field, Object> values) throws FileNotFoundException, IOException, IllegalArgumentException, FeedException, ParseException { + + File f = getFile(url); + + if (!f.exists()) { + throw new FileNotFoundException("file not existing " + f); + } + + // block until can acquire lock + FileLock lock = acquireLock(f); + + // temporary file where to write feed + + File tmpFile = null; + + try { + + SyndFeed feed = addItemToFeed(url, nbEntries, values); + + // save feed into a tmp file + + tmpFile = new File(f.getParentFile(), "tmp_" + f.getName() + "_" + System.nanoTime() + ".xml"); + + RSSIOUtil.saveFeed(tmpFile, feed); + + // move tmpFile to real file + tmpFile.renameTo(f); + + } finally { + releaseLock(f,lock); + } + } + + protected SyndFeed createFeed(FeedType type, Map<Field, Object> values) throws ParseException { + SyndFeed feed = new SyndFeedImpl(); + feed.setFeedType(type.getType()); + feed.setEncoding("utf-8"); + for (Entry<Field, Object> entry : values.entrySet()) { + Field field = entry.getKey(); + String name = feedProperties.get(field); + if (name == null) { + // this field is not managed + log.warn("the field " + field + " is not managed in feed"); + continue; + } + Object value = entry.getValue(); + Object realValue = null; + switch (field) { + case TIME: + realValue = DATE_PARSER.parse((String)value); + break; + default: + realValue = value; + } + + setFieldValue(feed, name, realValue); + } + + return feed; + } + + protected SyndEntry createFeedItem(Map<Field, Object> values) throws ParseException { + + SyndEntry feedEntry = new SyndEntryImpl(); + + for (Entry<Field, Object> entry : values.entrySet()) { + Field field = entry.getKey(); + String name = itemProperties.get(field); + if (name == null) { + // this field is not managed + log.warn("the field " + field + " is not managed in item"); + continue; + } + Object value = entry.getValue(); + Object realValue = null; + switch (field) { + case TIME: + realValue = DATE_PARSER.parse((String) value); + break; + case DESCRIPTION: + SyndContent description = new SyndContentImpl(); + description.setType("text/plain"); + description.setValue(String.valueOf(value)); + realValue = description; + break; + default: + realValue = value; + } + setFieldValue(feedEntry, name, realValue); + } + + return feedEntry; + } + + protected SyndFeed addItemToFeed(URL url, int nbEntries, Map<Field, Object> values) throws IOException, IllegalArgumentException, FeedException, ParseException { + SyndFeed feed = RSSIOUtil.readFeed(url); + List entries = feed.getEntries(); + // always sort by publication date + java.util.Collections.sort(entries, new FeedEntryComparator()); + // keep only nbEntries -1 entries + while (entries.size() > nbEntries - 1) { + entries.remove(0); + } + SyndEntry item = createFeedItem(values); + entries.add(item); + + return feed; + } + + /** + * Obtain the file from his url location. + * + * @param url location of the file + * @return the file + * @throws java.lang.IllegalStateException if uri is not sytax valid + */ + protected File getFile(URL url) throws IllegalStateException { + try { + File f = new File(url.toURI()); + + return f; + } catch (URISyntaxException e) { + throw new IllegalStateException("could not obtain file from url " + url, e); + } + } + + protected FileLock acquireLock(File f) throws IOException, FileNotFoundException { + File lockFile = new File(f.getParentFile(), f.getName() + ".lock"); + + /*if (!lockFile.exists()) lockFile.createNewFile(); */ + // open file for writing only + FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel(); + + // block until can acquire lock + FileLock lock = channel.lock(); + + // temporary file where to write feed + return lock; + } + + protected void releaseLock(File f,FileLock lock) throws IOException { + // release lock + lock.release(); + // close channel + lock.channel().close(); + // delete file lock + File lockFile = new File(f.getParentFile(), f.getName() + ".lock"); + lockFile.delete(); + } + + protected void setFieldValue(Object dst, String name, Object value) { + if (value == null) { + // null value is not managed + log.warn("null value for field " + name + " is not managed"); + return; + } + try { + BeanUtils.setProperty(dst, name, value); + } catch (Exception ex) { + log.warn("could not access property " + name, ex); + } + } +} Added: trunk/lutinrss/src/test/java/org/codelutin/rss/RSSGeneratorHelperTest.java =================================================================== --- trunk/lutinrss/src/test/java/org/codelutin/rss/RSSGeneratorHelperTest.java (rev 0) +++ trunk/lutinrss/src/test/java/org/codelutin/rss/RSSGeneratorHelperTest.java 2008-05-29 21:42:11 UTC (rev 71) @@ -0,0 +1,71 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.codelutin.rss; + +import com.sun.syndication.feed.synd.SyndFeed; +import java.io.File; +import java.util.Map; +import junit.framework.TestCase; + +/** + * + * @author tony + */ +public class RSSGeneratorHelperTest extends TestCase { + + RSSGeneratorHelper helper; + + + @Override + protected void setUp() throws Exception { + super.setUp(); + helper = RSSGeneratorHelper.getInstance(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testCreateFeed() throws Exception { + File f = getFeedFile(); + + try{ + Map<Field, Object> values = new java.util.HashMap<Field,Object>(); + values.put(Field.NAME, "feedName : "+f.getName()); + values.put(Field.DESCRIPTION, "feedDescription : "+f.getName()); + values.put(Field.LINK, f.toURI().toURL()); + values.put(Field.TIME,RSSGeneratorHelper.DATE_PARSER.format(new java.util.Date())); + + assertFalse(f.exists()); + RSSGeneratorHelper.log.info("feedFile : "+f); + helper.createFeedFile(f.toURI().toURL(), FeedType.RSS_1_0, values); + // lock is delete + assertFalse(new File(f.getAbsolutePath()+".lock").exists()); + // file exist + assertTrue(f.exists()); + //TODO test feed + SyndFeed feed = RSSIOUtil.readFeed(f.toURI().toURL()); + RSSGeneratorHelper.log.info(feed); + assertEquals(feed.getTitle(), values.get(Field.NAME)); + assertEquals(feed.getDescription(), values.get(Field.DESCRIPTION)); + assertEquals(feed.getPublishedDate(), RSSGeneratorHelper.DATE_PARSER.parse((String)values.get(Field.TIME))); + } finally { + f.deleteOnExit(); + } + + } + + public void testAddFeed() throws Exception { + + } + + protected File getFeedFile() { + File feedFile = new File("/tmp/" + getClass().getSimpleName() + "-" + System.nanoTime() + ".xml"); + return feedFile; + } + +}