View Javadoc

1   package org.musicontroller.core.jobs;
2   
3   import java.io.File;
4   import java.io.FileNotFoundException;
5   import java.io.IOException;
6   import java.io.InputStream;
7   import java.net.URI;
8   import java.net.URISyntaxException;
9   import java.net.URLConnection;
10  import java.util.Collection;
11  import java.util.HashSet;
12  import java.util.Set;
13  
14  import org.apache.log4j.Logger;
15  import org.hibernate.FlushMode;
16  import org.hibernate.HibernateException;
17  import org.hibernate.Session;
18  import org.hibernate.SessionFactory;
19  import org.hibernate.Transaction;
20  import org.musicontroller.core.Band;
21  import org.musicontroller.core.Contract_PS;
22  import org.musicontroller.core.Keyword;
23  import org.musicontroller.core.Playlist;
24  import org.musicontroller.core.Song;
25  import org.musicontroller.dao.BagAndKeywordUtils;
26  import org.musicontroller.dao.Dao;
27  import org.musicontroller.importer.ImporterException;
28  import org.musicontroller.importer.ImporterImpl;
29  import org.musicontroller.importer.MP3InspectorJID3Lib;
30  import org.musicontroller.importer.MediafileInspector;
31  import org.musicontroller.importer.MusicArchiveEntryBean;
32  import org.musicontroller.rss.RssDAO;
33  import org.springframework.orm.hibernate3.SessionHolder;
34  import org.springframework.transaction.support.TransactionSynchronizationManager;
35  import org.varienaja.util.FileOperations;
36  
37  /**
38   * This class provides the functionality to import a Podcast-Playlist into the
39   * database. It does so asynchronously. It's done when it's done :-)
40   * <p>Since this Jon runs asynchronously, it cannot rely on the OpenSessionInView.
41   * Therefore transaction-handling is explicitly implemented in this class.</p>
42   * @author Varienaja
43   */
44  public class PodcastUpdateJob implements Runnable {
45  	private static final Logger LOG = Logger.getLogger(PodcastUpdateJob.class);
46  	
47  	private String _podcasturl;
48  	private static Dao _dao;
49  	
50  
51  	public void setPodcastURL(String url) {
52  		_podcasturl = url;
53  	}
54  	
55  	public void setDao(Dao dao) {
56  		_dao = dao;
57  	}
58  	
59  	/*
60  	 * (non-Javadoc)
61  	 * @see java.lang.Runnable#run()
62  	 */
63  	public void run() {
64  		if (_podcasturl==null) {
65  			LOG.debug("null-podcast, nothing can be imported.");
66  			return;
67  		}
68  		if (_dao==null) {
69  			LOG.debug("Dao not set, nothing can be imported.");
70  			return;
71  		}
72  		LOG.debug("Updating from: " + _podcasturl);
73  		
74  		SessionFactory factory = _dao.getSessionFactory2();
75  		Session session = null;
76  	    Transaction tx = null;
77  		try {
78  			session = factory.openSession();
79  			// Because we're messing a lot with a Playlist here, we want manual
80  			// flushes. Otherwise, Hibernate will sometimes issue an auto-flush
81  			// in situations where the Playlist is not yet in a consistent
82  			// state, generating SQL-errors. In the end, after our modifications
83  			// we're sure that the Playlist is consistent, and we issue the
84  			// flush manually.
85  			session.setFlushMode(FlushMode.MANUAL);
86  		    TransactionSynchronizationManager.bindResource(factory, new SessionHolder(session));
87  		    
88  			tx = session.beginTransaction();
89  			Playlist playlist = extractPodcastInfo(_podcasturl);
90  			
91  			tx = session.beginTransaction();
92  			if (playlist!=null) {
93  				updatePodcastPlaylist(playlist);
94  				session.flush();
95  			}
96  		} catch (HibernateException e) {
97  			LOG.error(e.getMessage());
98  			if (tx!=null) {
99  				tx.rollback();
100 			}
101 		} catch (Exception e) {
102 			e.printStackTrace();
103 		} finally {
104 			if (session!=null) {
105 				session.close();
106 				TransactionSynchronizationManager.unbindResource(factory);
107 			}
108 		}
109 	}
110 	
111 	private Playlist extractPodcastInfo(String podcasturl) {
112 		RssDAO rssdao = new RssDAO();
113 		rssdao.setDao(_dao);
114 		return rssdao.readPlaylistFromRss(podcasturl);
115 	}
116 
117 	/**
118 	 * Creates (if not already exists) or updates a Playlist.
119 	 * @param Playlist A Playlist object. May be an entirely new Playlist or
120 	 * a Playlist that is already to MusiController, with extra songs added to 
121 	 * it. 
122 	 */
123 	private void updatePodcastPlaylist(Playlist playlist) {
124 		for (Contract_PS contract : playlist.getSongs()) {
125 			Song song = contract.getSong();
126 			Band band = song.getBand();
127 			if (band.getId()<0) {
128 				_dao.save(band);
129 				LOG.debug("Band " + band.getName() + " created...");
130 			}
131 			
132 			if (song.getId()<0) {
133 				Set<String> keywords = new HashSet<String>();
134 				for (Keyword kw : song.getKeywordbag().getKeywords()) {
135 					keywords.add(kw.getName());
136 				}
137 				
138 				InputStream in = null;
139 				try {
140 					LOG.debug("Downloading song " + song.getName() + " from " + song.getLink() + " to a temporary location...");
141 					URLConnection urlc = new URI(song.getLink().getUrl()).toURL().openConnection();
142 					in = urlc.getInputStream();
143 					String tmpName = FileOperations.tmpFile(in, "importingPodcastEntry");
144 					//We do not trust the length reported from the rss, so we calculate it ourselves
145 					MediafileInspector inspector = new MP3InspectorJID3Lib(new File(tmpName));
146 					song.setLength(inspector.getSonglength());
147 					LOG.debug("Download to " + tmpName + " complete.");
148 					
149 					//Construct a MusicArchiveBean in order to be able to reuse
150 					//code to move it into the Library.
151 					MusicArchiveEntryBean bean = new MusicArchiveEntryBean();
152 					bean.setEntryName(tmpName);
153 					bean.setBandName(band.getName());
154 					bean.setSongName(song.getName());
155 					String libraryName = ImporterImpl.moveMP3ToLibrary(bean);
156 					song.getLink().setUrl(libraryName);
157 					_dao.save(song.getLink());
158 					LOG.debug("Moved song " + song.getName() + " to the MC library at: " + libraryName);
159 
160 					song.setKeywordbag(BagAndKeywordUtils.getKeywordBag(song.getKeywordbag().getKeywords()));
161 					_dao.save(song);
162 					
163 					LOG.debug("Added song " + song.getName() + " to the playlist at position: " + contract.getRowno());
164 				} catch (FileNotFoundException e) {
165 					LOG.error("Error importing song " + song + ": " + e.getMessage());
166 				} catch (IOException e) {
167 					LOG.error("Error importing song " + song + ": " + e.getMessage());
168 				} catch (URISyntaxException e) {
169 					LOG.error("Error importing song " + song + ": " + e.getMessage());
170 				} catch (ImporterException e) {
171 					LOG.error("Error importing song " + song + ": " + e.getMessage());
172 				} finally {
173 					if (in!=null) {
174 						try {
175 							in.close();
176 						} catch (IOException e) {
177 							LOG.error("Error closing inputstream: " + e.getMessage());
178 						}
179 					}
180 				}
181 			}
182 		}
183 		if (playlist.getLink().getId()<0) {
184 			_dao.save(playlist.getLink());
185 		}
186 		_dao.save(playlist);
187 		LOG.debug("Importing podcast done!");
188 	}
189 	
190 	/**
191 	 * Executes the updating of all Podcasts. 
192 	 */
193 	@SuppressWarnings("unchecked")
194 	public void execute() {
195 		LOG.debug("Updating of Podcasts started...");
196 		String hql = "select p.link.url from Playlist p where p.link is not null";
197 		Collection<String> podcastURLs = _dao.search(hql, null, 0);
198 		for (String podcastURL : podcastURLs) {
199 			_podcasturl = podcastURL;
200 			run();
201 		}
202 		LOG.debug("Updating of Podcasts done.");
203 	}
204 
205 }