diff --git a/src/main/java/musicplayer/PlayerGUI.java b/src/main/java/musicplayer/PlayerGUI.java index a9dda10..4a0215a 100644 --- a/src/main/java/musicplayer/PlayerGUI.java +++ b/src/main/java/musicplayer/PlayerGUI.java @@ -5,8 +5,7 @@ import com.google.inject.Inject; import com.google.inject.Injector; import musicplayer.callbacks.LibraryCallbackInterface; import musicplayer.callbacks.PlayerCallbackInterface; -import musicplayer.db.DatabaseManager; -import musicplayer.db.Gateway; +import musicplayer.db.IDatabase; import musicplayer.model.Album; import musicplayer.model.Artist; import musicplayer.model.HasSongs; @@ -52,6 +51,7 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf private JSlider seekBar; private final JComboBox libraryDisplayType = new JComboBox<>(); private final IPlayer player; + private final IDatabase database; private final PlaylistTableModel playlistTableModel = new PlaylistTableModel(new ArrayList<>()); private static final DefaultMutableTreeNode updatingNode = new DefaultMutableTreeNode(); private boolean libraryUpdating = false; @@ -60,8 +60,9 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf private final Map libraryDisplayVariants = createDisplayVariantMap(); @Inject - public PlayerGUI(IPlayer player) { + public PlayerGUI(IPlayer player, IDatabase database) { this.player = player; + this.database = database; createUI(); resetTree(); Thread seekBarUpdater = new Thread(() -> { @@ -102,7 +103,6 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf } public static void main(String[] args) { - DatabaseManager.init(); Injector injector = Guice.createInjector(); PlayerGUI playerGUI = injector.getInstance(PlayerGUI.class); JFrame frame = new JFrame(); @@ -269,7 +269,7 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf addNodeToTreeModel(model, parentNode, updatingNode); libraryView.setModel(model); List dirs = ConfigManager.getLibraryDirectories().stream().map(File::toPath).collect(Collectors.toList()); - LibraryUtils.processSongsWithCallback(dirs, this); + LibraryUtils.processSongsWithCallback(database, dirs, this); } @Override @@ -290,9 +290,9 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf */ private Map createDisplayVariantMap() { Map value = new HashMap<>(); - value.put("Song", () -> Gateway.listAllT(Song.class).ifPresent(this::populateLibraryWithSongsOnly)); - value.put("Album/Song", () -> Gateway.listAllT(Album.class).ifPresent(this::populateLibraryWithGroupedSongs)); - value.put("Artist/Song", () -> Gateway.listAllT(Artist.class).ifPresent(this::populateLibraryWithGroupedSongs)); + value.put("Song", () -> database.listAllT(Song.class).ifPresent(this::populateLibraryWithSongsOnly)); + value.put("Album/Song", () -> database.listAllT(Album.class).ifPresent(this::populateLibraryWithGroupedSongs)); + value.put("Artist/Song", () -> database.listAllT(Artist.class).ifPresent(this::populateLibraryWithGroupedSongs)); return value; } diff --git a/src/main/java/musicplayer/db/DatabaseManager.java b/src/main/java/musicplayer/db/DatabaseManager.java deleted file mode 100644 index bf36c17..0000000 --- a/src/main/java/musicplayer/db/DatabaseManager.java +++ /dev/null @@ -1,82 +0,0 @@ -package musicplayer.db; - -import musicplayer.ConfigManager; -import musicplayer.model.Album; -import musicplayer.model.Artist; -import musicplayer.model.Song; -import org.hibernate.Session; -import org.hibernate.SessionFactory; -import org.hibernate.cfg.Configuration; - -import java.util.Properties; - - -/** - * Singleton for managing connection to Hibernate. - */ -public class DatabaseManager { - private static final DatabaseManager ourInstance = new DatabaseManager(); - - private SessionFactory sessionFactory; - - public static DatabaseManager getInstance() { - return ourInstance; - } - - private DatabaseManager() { - } - - /** - * Open a SessionFactory to the database as defined in hibernate.cfg.xml - */ - public static void init() { - if (getInstance().sessionFactory != null) - getInstance().sessionFactory.close(); - Properties properties = new Properties(); - properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect"); - properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver"); - properties.put("hibernate.connection.url", ConfigManager.getDatabaseConnectionString()); - properties.put("hibernate.hbm2ddl.auto", "update"); - properties.put("hibernate.enable_lazy_load_no_trans", "true"); - getInstance().sessionFactory = new Configuration() - .addProperties(properties) - .addAnnotatedClass(Album.class) - .addAnnotatedClass(Artist.class) - .addAnnotatedClass(Song.class) - .buildSessionFactory(); - } - - /** - * Open a SessionFactory to an in-memory database for testing - */ - public static void testMode() { - if (getInstance().sessionFactory != null) - getInstance().sessionFactory.close(); - Properties properties = new Properties(); - properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect"); - properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver"); - properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:."); - properties.put("hibernate.hbm2ddl.auto", "create-drop"); - properties.put("hibernate.enable_lazy_load_no_trans", "true"); - getInstance().sessionFactory = new Configuration() - .addProperties(properties) - .addAnnotatedClass(Album.class) - .addAnnotatedClass(Artist.class) - .addAnnotatedClass(Song.class) - .buildSessionFactory(); - } - - - /** - * @return Session for database interaction. - */ - public Session getSession() { - return sessionFactory.openSession(); - } - - @SuppressWarnings("CloneDoesntCallSuperClone") - @Override - public Object clone() throws CloneNotSupportedException { - throw new CloneNotSupportedException(); - } -} diff --git a/src/main/java/musicplayer/db/Gateway.java b/src/main/java/musicplayer/db/HibernateDatabase.java similarity index 54% rename from src/main/java/musicplayer/db/Gateway.java rename to src/main/java/musicplayer/db/HibernateDatabase.java index beb8723..5f6e6e7 100644 --- a/src/main/java/musicplayer/db/Gateway.java +++ b/src/main/java/musicplayer/db/HibernateDatabase.java @@ -1,26 +1,77 @@ package musicplayer.db; +import musicplayer.ConfigManager; import musicplayer.model.*; import musicplayer.util.AlbumArtExtractor; import org.hibernate.Criteria; import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; import org.hibernate.criterion.Restrictions; import java.io.File; import java.util.List; import java.util.Optional; +import java.util.Properties; + /** - * Contains database queries. + * Class for managing connection to Hibernate. */ -public class Gateway { +class HibernateDatabase implements IDatabase{ + + private static SessionFactory sessionFactory; + + /** + * Create a database connection. + */ + public HibernateDatabase() { + this(false); + } + + /** + * Create a database connection. + * @param testMode Enable test mode. + */ + public HibernateDatabase(boolean testMode){ + Properties properties = new Properties(); + if (sessionFactory != null) + sessionFactory.close(); + if(testMode){ + properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect"); + properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver"); + properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:."); + properties.put("hibernate.hbm2ddl.auto", "create-drop"); + properties.put("hibernate.enable_lazy_load_no_trans", "true"); + } + else{ + properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect"); + properties.put("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver"); + properties.put("hibernate.connection.url", ConfigManager.getDatabaseConnectionString()); + properties.put("hibernate.hbm2ddl.auto", "update"); + properties.put("hibernate.enable_lazy_load_no_trans", "true"); + } + sessionFactory = new Configuration() + .addProperties(properties) + .addAnnotatedClass(Album.class) + .addAnnotatedClass(Artist.class) + .addAnnotatedClass(Song.class) + .buildSessionFactory(); + } + + /** + * @return Session for database interaction. + */ + private Session getSession() { + return sessionFactory.openSession(); + } /** * @param name Name of the album to find. * @return Album found with name or Optional.empty() */ - public static Optional getOneAlbum(String name) { - try (Session session = DatabaseManager.getInstance().getSession()) { + public Optional getOneAlbum(String name) { + try (Session session = getSession()) { Criteria criteria = session.createCriteria(Album.class); Album album = (Album) criteria.add(Restrictions.eq("name", name)).uniqueResult(); return (album == null) ? Optional.empty() : Optional.of(album); @@ -31,8 +82,8 @@ public class Gateway { * @param name Name of the artist to find. * @return Artist found with name or Optional.empty() */ - public static Optional getOneArtist(String name) { - try (Session session = DatabaseManager.getInstance().getSession()) { + public Optional getOneArtist(String name) { + try (Session session = getSession()) { Criteria criteria = session.createCriteria(Artist.class); Artist artist = (Artist) criteria.add(Restrictions.eq("name", name)).uniqueResult(); return (artist == null) ? Optional.empty() : Optional.of(artist); @@ -43,8 +94,8 @@ public class Gateway { * @param metadata Metadata of song to find. * @return Song found with metadata or Optional.empty() */ - public static Optional getOneSong(ExtractedMetadata metadata){ - try (Session session = DatabaseManager.getInstance().getSession()) { + public Optional getOneSong(ExtractedMetadata metadata){ + try (Session session = getSession()) { Song song = (Song)session.createCriteria(Song.class) .add(Restrictions.eq("songFile", metadata.getSongFile())).uniqueResult(); return (song == null) ? Optional.empty() : Optional.of(song); @@ -56,8 +107,8 @@ public class Gateway { * @param typeClass Class representing the type of items to find. * @return List of all items in the database of class typeClass. */ - public static Optional> listAllT(Class typeClass){ - try (Session session = DatabaseManager.getInstance().getSession()) { + public Optional> listAllT(Class typeClass){ + try (Session session = getSession()) { @SuppressWarnings("unchecked") List output = session.createCriteria(typeClass).list(); return (output == null || output.isEmpty()) ? Optional.empty() : Optional.of(output); @@ -69,8 +120,8 @@ public class Gateway { * If the song already exists it will be updated instead. * @param metadata New song information. */ - public static void addSong(ExtractedMetadata metadata) { - try (Session session = DatabaseManager.getInstance().getSession()) { + public void addSong(ExtractedMetadata metadata) { + try (Session session = getSession()) { session.beginTransaction(); Optional albumObj = getOneAlbum(metadata.getAlbum()); if (!albumObj.isPresent()) { @@ -92,4 +143,12 @@ public class Gateway { session.getTransaction().commit(); } } + + public void addSong(Song song){ + try (Session session = getSession()) { + session.beginTransaction(); + session.save(song); + session.getTransaction().commit(); + } + } } diff --git a/src/main/java/musicplayer/db/IDatabase.java b/src/main/java/musicplayer/db/IDatabase.java new file mode 100644 index 0000000..8081ccc --- /dev/null +++ b/src/main/java/musicplayer/db/IDatabase.java @@ -0,0 +1,17 @@ +package musicplayer.db; + +import com.google.inject.ImplementedBy; +import musicplayer.model.*; + +import java.util.List; +import java.util.Optional; + +@ImplementedBy(HibernateDatabase.class) +public interface IDatabase { + Optional getOneAlbum(String name); + Optional getOneArtist(String name); + Optional getOneSong(ExtractedMetadata metadata); + Optional> listAllT(Class typeClass); + void addSong(ExtractedMetadata metadata); + void addSong(Song song); +} diff --git a/src/main/java/musicplayer/player/GStreamerPlayer.java b/src/main/java/musicplayer/player/GStreamerPlayer.java index 484012e..eb4ecaf 100644 --- a/src/main/java/musicplayer/player/GStreamerPlayer.java +++ b/src/main/java/musicplayer/player/GStreamerPlayer.java @@ -13,7 +13,7 @@ import org.gstreamer.elements.PlayBin2; import java.io.File; import java.util.Optional; -public class GStreamerPlayer implements IPlayer{ +class GStreamerPlayer implements IPlayer{ private final PlayBin2 playBin; diff --git a/src/main/java/musicplayer/util/LibraryUtils.java b/src/main/java/musicplayer/util/LibraryUtils.java index 844eccb..72eaffe 100644 --- a/src/main/java/musicplayer/util/LibraryUtils.java +++ b/src/main/java/musicplayer/util/LibraryUtils.java @@ -1,7 +1,7 @@ package musicplayer.util; import musicplayer.callbacks.LibraryCallbackInterface; -import musicplayer.db.Gateway; +import musicplayer.db.IDatabase; import musicplayer.model.ExtractedMetadata; import org.jaudiotagger.audio.AudioFileIO; import org.jaudiotagger.audio.exceptions.CannotReadException; @@ -26,7 +26,7 @@ public final class LibraryUtils { * @param rootDirectory Folder(s) containing music files to index. * @param callbackInterface Object to send callback data to, set to null if the application doesn't require update data. */ - public static void processSongsWithCallback(List rootDirectory, LibraryCallbackInterface callbackInterface){ + public static void processSongsWithCallback(IDatabase database, List rootDirectory, LibraryCallbackInterface callbackInterface){ boolean callbacksEnabled = callbackInterface != null; updaterThread = new Thread(() -> { rootDirectory.forEach(dir -> { @@ -34,7 +34,7 @@ public final class LibraryUtils { Files.walk(dir) .filter(f -> f.toString().matches(musicFileExtensionRegex)).map(LibraryUtils::autoParse) .forEach(x -> x.ifPresent(i -> { - Gateway.addSong(i); + database.addSong(i); if (callbacksEnabled) callbackInterface.currentlyUpdating(i.toString()); })); diff --git a/src/test/java/musicplayer/db/GatewayTest.java b/src/test/java/musicplayer/db/GatewayTest.java index 33eb292..da5db84 100644 --- a/src/test/java/musicplayer/db/GatewayTest.java +++ b/src/test/java/musicplayer/db/GatewayTest.java @@ -4,11 +4,9 @@ import musicplayer.model.Album; import musicplayer.model.Artist; import musicplayer.model.ExtractedMetadata; import musicplayer.model.Song; -import org.hibernate.Session; import org.jaudiotagger.tag.FieldKey; import org.jaudiotagger.tag.Tag; import org.jaudiotagger.tag.id3.ID3v23Tag; -import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -20,46 +18,37 @@ import static org.junit.Assert.assertTrue; public class GatewayTest { - private Session session; + private IDatabase database; @Before public void setUp(){ - DatabaseManager.testMode(); - session = DatabaseManager.getInstance().getSession(); - } - - @After - public void tearDown(){ - session.close(); - session = null; + database = new HibernateDatabase(true); } @Test public void testGetOneAlbum() throws Exception { String albumName = "Test Album"; Album testAlbum = new Album(albumName); - session.beginTransaction(); - session.save(testAlbum); - session.getTransaction().commit(); - assertTrue(Gateway.getOneAlbum(albumName).isPresent()); - Album retrievedAlbum = Gateway.getOneAlbum(albumName).get(); + Song song = new Song("1", "1", "s1", new Artist("art"), testAlbum, "", ""); + database.addSong(song); + assertTrue(database.getOneAlbum(albumName).isPresent()); + Album retrievedAlbum = database.getOneAlbum(albumName).get(); assertEquals(testAlbum, retrievedAlbum); } @Test public void testGetOneAlbumEmptyDB() throws Exception { - assertTrue(!Gateway.getOneAlbum("").isPresent()); + assertTrue(!database.getOneAlbum("").isPresent()); } @Test public void testGetOneArtist() throws Exception { String artistName = "Test Artist"; Artist testArtist = new Artist(artistName); - session.beginTransaction(); - session.save(testArtist); - session.getTransaction().commit(); - assertTrue(Gateway.getOneArtist(artistName).isPresent()); - Artist retrievedArtist = Gateway.getOneArtist(artistName).get(); + Song song = new Song("1", "1", "s1", testArtist, new Album("alb"), "", ""); + database.addSong(song); + assertTrue(database.getOneArtist(artistName).isPresent()); + Artist retrievedArtist = database.getOneArtist(artistName).get(); assertEquals(testArtist, retrievedArtist); } @@ -70,12 +59,10 @@ public class GatewayTest { Song song1 = new Song("1", "1", "s1", artist1, new Album("a"), "", ""); Song song2 = new Song("2", "1", "s2", artist1, new Album("b"), "", ""); Song song3 = new Song("1", "1", "t1", artist2, new Album("c"), "", ""); - session.beginTransaction(); - session.save(song1); - session.save(song2); - session.save(song3); - session.getTransaction().commit(); - List retrievedArtists = Gateway.listAllT(Artist.class).get(); + database.addSong(song1); + database.addSong(song2); + database.addSong(song3); + List retrievedArtists = database.listAllT(Artist.class).get(); int index1 = retrievedArtists.indexOf(artist1); int index2 = retrievedArtists.indexOf(artist2); assertTrue(retrievedArtists.get(index1).getSongs().size() == 2); @@ -87,7 +74,7 @@ public class GatewayTest { @Test public void testGetOneArtistEmptyDB() throws Exception { - assertTrue(!Gateway.getOneArtist("").isPresent()); + assertTrue(!database.getOneArtist("").isPresent()); } @Test @@ -95,13 +82,11 @@ public class GatewayTest { Song song1 = new Song("1", "1", "s1", new Artist("a"), new Album("a"), "", ""); Song song2 = new Song("2", "1","s2", new Artist("b"), new Album("a"), "", ""); Song song3 = new Song("1", "1","t1", new Artist("c"), new Album("b"), "", ""); - session.beginTransaction(); - session.save(song1); - session.save(song2); - session.save(song3); - session.getTransaction().commit(); - assertTrue(Gateway.listAllT(Song.class).isPresent()); - List result = Gateway.listAllT(Song.class).get(); + database.addSong(song1); + database.addSong(song2); + database.addSong(song3); + assertTrue(database.listAllT(Song.class).isPresent()); + List result = database.listAllT(Song.class).get(); assertTrue(result.size() == 3); assertTrue(result.contains(song1)); assertTrue(result.contains(song2)); @@ -110,7 +95,7 @@ public class GatewayTest { @Test public void testListAllSongsEmptyDB() throws Exception { - assertTrue(!Gateway.listAllT(Song.class).isPresent()); + assertTrue(!database.listAllT(Song.class).isPresent()); } @Test @@ -120,12 +105,10 @@ public class GatewayTest { Song song1 = new Song("1", "1", "s1", new Artist("a"), album1, "", ""); Song song2 = new Song("2", "1", "s2", new Artist("b"), album1, "", ""); Song song3 = new Song("1", "1", "t1", new Artist("c"), album2, "", ""); - session.beginTransaction(); - session.save(song1); - session.save(song2); - session.save(song3); - session.getTransaction().commit(); - List retrievedAlbums = Gateway.listAllT(Album.class).get(); + database.addSong(song1); + database.addSong(song2); + database.addSong(song3); + List retrievedAlbums = database.listAllT(Album.class).get(); int index1 = retrievedAlbums.indexOf(album1); int index2 = retrievedAlbums.indexOf(album2); assertTrue(retrievedAlbums.get(index1).getSongs().size() == 2); @@ -145,8 +128,8 @@ public class GatewayTest { tags.addField(FieldKey.GENRE, "Test Genre"); tags.addField(FieldKey.DISC_NO, "100"); ExtractedMetadata metadata = new ExtractedMetadata(tags, new File("")); - Gateway.addSong(metadata); - Song song = (Song)session.createCriteria(Song.class).uniqueResult(); + database.addSong(metadata); + Song song = database.getOneSong(metadata).get(); assertEquals(song.getAlbum().getName(), metadata.getAlbum()); assertEquals(song.getArtist().getName(), metadata.getArtist()); assertEquals(song.getTrackNumber(), Integer.valueOf(1));