/** * Copyright (C) 2013 Johannes Schnatterer * * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This file is part of nusic. * * nusic is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * nusic is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with nusic. If not, see <http://www.gnu.org/licenses/>. */ package info.schnatterer.nusic.core.impl; import info.schnatterer.nusic.core.ArtistService; import info.schnatterer.nusic.core.DeviceMusicService; import info.schnatterer.nusic.core.PreferencesService; import info.schnatterer.nusic.core.RemoteMusicDatabaseService; import info.schnatterer.nusic.core.ServiceException; import info.schnatterer.nusic.core.SyncReleasesService; import info.schnatterer.nusic.core.event.ArtistProgressListener; import info.schnatterer.nusic.core.event.ProgressListener; import info.schnatterer.nusic.core.event.ProgressUpdater; import info.schnatterer.nusic.core.i18n.CoreMessageKey; import info.schnatterer.nusic.data.DatabaseException; import info.schnatterer.nusic.data.model.Artist; import java.util.Calendar; import java.util.Date; import java.util.HashSet; import java.util.Set; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Default implementation of {@link SyncReleasesService}. * * @author schnatterer * */ public class SyncReleasesServiceImpl implements SyncReleasesService { private static final Logger LOG = LoggerFactory .getLogger(SyncReleasesServiceImpl.class); @Inject private RemoteMusicDatabaseService remoteMusicDatabaseService; @Inject private DeviceMusicService deviceMusicService; @Inject private PreferencesService preferencesService; @Inject private ArtistService artistService; private Set<ProgressListener<Artist, Boolean>> listenerList = new HashSet<ProgressListener<Artist, Boolean>>(); private ProgressUpdater<Artist, Boolean> progressUpdater = new ProgressUpdater<Artist, Boolean>( listenerList) { }; @Override public void syncReleases() { Date startDate = createStartDate(preferencesService .getDownloadReleasesTimePeriod()); // Use a date before the refresh to store afterwards in order to Date dateCreated = new Date(); refreshReleases(startDate, null); preferencesService.setLastReleaseRefresh(dateCreated); } private Date createStartDate(int months) { if (months <= 0) { return null; } Calendar cal = Calendar.getInstance(); cal.add(Calendar.MONTH, -months); return cal.getTime(); } private void refreshReleases(Date startDate, Date endDate) { // TODO create service for checking wifi and available internet connection Artist[] artists = queryArtists(); if (artists == null) { return; } progressUpdater.progressStarted(artists.length); ServiceException potentialException = null; for (int i = 0; i < artists.length; i++) { try { potentialException = processArtist(artists, i, startDate, endDate); if (potentialException != null && potentialException.getCause() instanceof DatabaseException) { // Allow for displaying errors to the user. progressUpdater.progressFailed(artists[i], i + 1, new AndroidServiceException(CoreMessageKey.ERROR_WRITING_TO_DB, potentialException), null); return; } } catch (Exception e) { LOG.warn("Unexpected exception during sync, cancelling sync", e); progressUpdater.progressFailed(artists[i], i + 1, e, null); return; } progressUpdater.progress(artists[i], i + 1, potentialException); } progressUpdater.progressFinished(true); } /** * Finds releases for an artists and stores them. Logs {@link ServiceException}s and propagates * them to the {@link #progressUpdater}. * @return any {@link ServiceException} that might have occurred, or {@code null} if none occurred */ private ServiceException processArtist(Artist[] artists, int i, Date startDate, Date endDate) { Artist artist = artists[i]; /* TODO find out if its more efficient to concat all artist in one big query and then * process it page by page (keep URL limit of 2048 chars in mind) */ try { artist = remoteMusicDatabaseService.findReleases(artist, startDate, endDate); if (artist == null) { LOG.warn("Artist {} of {} is null.", i, artists.length); } else if (artist.getReleases().size() > 0) { artistService.saveOrUpdate(artist); // After saving, release memory for releases artist.setReleases(null); } // Release memory for artist artists[i] = null; } catch (ServiceException e) { LOG.warn(e.getMessage(), e.getCause()); return e; } return null; } /** * Queries artists from device. Logs all errors and propagates to {@link #progressUpdater}. * * @return a list of artists. Empty list if none found, <code>null</code> on error. */ private Artist[] queryArtists() { Artist[] artists = null; try { artists = deviceMusicService.getArtists(); if (artists == null) { LOG.warn("No artists were returned. No music files on device?"); artists = new Artist[0]; } } catch (Exception e) { LOG.warn("Error querying artists from device", e); progressUpdater.progressFailed(null, 0, e, null); } return artists; } @Override public void addArtistProcessedListener(ArtistProgressListener l) { if (l != null) { listenerList.add(l); } } @Override public boolean removeArtistProcessedListener( ArtistProgressListener artistProcessedListener) { if (artistProcessedListener != null) { return listenerList.remove(artistProcessedListener); } else { return false; } } @Override public void removeArtistProcessedListeners() { listenerList.clear(); } }