From: Aron Roberts Date: Tue, 11 Aug 2009 20:02:50 +0000 (+0000) Subject: CSPACE-336,CSPACE-321: Updated JDBC implementation of ID Service and associated tests... X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=3db29997799f38ea83b6f7dfe1d13404d6d385db;p=tmp%2Fjakarta-migration.git CSPACE-336,CSPACE-321: Updated JDBC implementation of ID Service and associated tests to partly reflect refactoring to use IDGenerators, rather than IDPatterns, as highest level objects in the service. --- diff --git a/services/id/service/src/main/java/org/collectionspace/services/id/IDServiceJdbcImpl.java b/services/id/service/src/main/java/org/collectionspace/services/id/IDServiceJdbcImpl.java index bfe0cbcb9..b835347a4 100644 --- a/services/id/service/src/main/java/org/collectionspace/services/id/IDServiceJdbcImpl.java +++ b/services/id/service/src/main/java/org/collectionspace/services/id/IDServiceJdbcImpl.java @@ -21,16 +21,26 @@ // of the ID Service. As a result, there will be some naming // inconsistencies throughout this source file. -// @TODO: Revise exception handling to return custom Exceptions, +// @TODO Revise exception handling to return custom Exceptions, // perhaps mirroring the subset of HTTP status codes returned. // // We're currently overloading existing core and extension Java Exceptions // in ways that are not consistent with their original semantic meaning. -// @TODO: Retrieve IDGenerators from the database (via JDBC or +// @TODO Get the JDBC driver classname and database URL from configuration; +// better yet, substitute Hibernate for JDBC for accessing database-managed persistence. + +// @TODO Remove any hard-coded dependencies on MySQL. + +// @TODO Determine how to restrict access to ID-related tables by role. + +// @TODO Retrieve IDGenerators from the database (via JDBC or // Hibernate) at initialization and refresh time. -// @TODO: Handle concurrency. +// @TODO Remove redundancy. If we're using JDBC, a great deal of JDBC code +// is replicated in each method below. + +// @TODO Handle concurrency. // // Right now, with each new request we're simply instantiating // a new IDPattern and returning its next ID. As a result, @@ -42,11 +52,12 @@ // // At that point, we'll also need to add code to handle concurrent requests. -// @TODO: Verify access (public, protected, or private) to service methods. +// @TODO Verify access (public, protected, or private) to service methods. -// @TODO: As long as we're using JDBC, use PreparedStatements, not Statements. +// @TODO As long as we're using JDBC, use PreparedStatements, not Statements, +// throughout the code below. -// @TODO: Re-consider beginnings of method names: +// @TODO Re-consider beginnings of method names: // - "store/get" versus: // - "store/retrieve" // - "save/read" (appears to be used by Hibernate), @@ -79,25 +90,123 @@ public class IDServiceJdbcImpl implements IDService { final Logger logger = LoggerFactory.getLogger(IDServiceJdbcImpl.class); - // @TODO Get the JDBC driver classname and database URL from configuration; - // better yet, substitute Hibernate for JDBC for accessing database-managed persistence. - - // @TODO: Remove any hard-coded dependencies on MySQL. - - // @TODO: Determine how to restrict access to ID-related tables by role. - final String JDBC_DRIVER_CLASSNAME = "com.mysql.jdbc.Driver"; - final String DATABASE_URL = "jdbc:mysql://localhost:3306/cspace"; + final String DATABASE_NAME = "cspace"; + final String DATABASE_URL = "jdbc:mysql://localhost:3306/" + DATABASE_NAME; final String DATABASE_USERNAME = "test"; final String DATABASE_PASSWORD = "test"; + final String TABLE_NAME = "id_generator"; ////////////////////////////////////////////////////////////////////// /** * Constructor (no-argument). */ public void IDServiceJdbcImpl() { + + // @TODO Decide when and how to fail at startup, or else to correct + // failure conditions automatically, when preconditions are not met. + + // init(); + } + + // @TODO init() and hasTable() are currently UNTESTED as of 2009-08-11T13:00-0700. + + ////////////////////////////////////////////////////////////////////// + /** + * Initializes the service. + * + * @throws IllegalStateException if one or more of the required preconditions + * for the service is not present, or is not in its required state. + */ + public void init() throws IllegalStateException { + + try { + boolean hasTable = hasTable(TABLE_NAME); + if (! hasTable) { + throw new IllegalStateException( + "Table " + "\'" + TABLE_NAME + "\'" + " could not be found in the database."); + } + } catch (IllegalStateException e) { + throw e; + } + } + ////////////////////////////////////////////////////////////////////// + /** + * Identifies whether a specified table exists in the database. + * + * @param tablename The name of a database table. + * + * @return True if the specified table exists in the database; + * false if the specified table does not exist in the database. + * + * @throws IllegalStateException if an error occurs while checking for the + * existence of the specified table. + */ + public boolean hasTable(String tablename) throws IllegalStateException { + + logger.debug("> in hasTable"); + + if (tablename == null || tablename.equals("")) { + return false; + } + + try { + Class.forName(JDBC_DRIVER_CLASSNAME).newInstance(); + } catch (ClassNotFoundException e) { + throw new IllegalStateException( + "Error finding JDBC driver class '" + + JDBC_DRIVER_CLASSNAME + + "' to set up database connection."); + } catch (InstantiationException e) { + throw new IllegalStateException( + "Error instantiating JDBC driver class '" + + JDBC_DRIVER_CLASSNAME + + "' to set up database connection."); + } catch (IllegalAccessException e) { + throw new IllegalStateException( + "Error accessing JDBC driver class '" + + JDBC_DRIVER_CLASSNAME + + "' to set up database connection."); + } + + Connection conn = null; + try { + + conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USERNAME, DATABASE_PASSWORD); + + Statement stmt = conn.createStatement(); + + final String CATALOG_NAME = null; + final String SCHEMA_NAME_PATTERN = null; + final String[] TABLE_TYPES = null; + ResultSet tablesMatchingTableName = + conn.getMetaData().getTables( + CATALOG_NAME, SCHEMA_NAME_PATTERN, tablename, TABLE_TYPES); + + boolean moreRows = tablesMatchingTableName.next(); + if (! moreRows) { + return false; + } else { + return true; + } + + } catch (SQLException e) { + throw new IllegalStateException( + "Error while checking for existance of tablebase table: " + e.getMessage()); + } finally { + try { + if (conn != null) { + conn.close(); + } + } catch(SQLException e) { + // Do nothing here + } + } + + } + ////////////////////////////////////////////////////////////////////// /** * Generates and returns a new ID associated with a specified ID generator. @@ -108,6 +217,11 @@ public class IDServiceJdbcImpl implements IDService { * @param csid An identifier for an ID generator. * * @return A new ID associated with the specified ID generator. + * + * @throws IllegalArgumentException if the provided csid is null or empty, + * or if the specified ID generator can't be found. + * + * @throws IllegalStateException if a storage-related error occurred. */ public String newID(String csid) throws IllegalArgumentException, IllegalStateException { diff --git a/services/id/service/src/test/java/org/collectionspace/services/id/test/IDServiceJdbcImplTest.java b/services/id/service/src/test/java/org/collectionspace/services/id/test/IDServiceJdbcImplTest.java index 383572751..a1847329f 100644 --- a/services/id/service/src/test/java/org/collectionspace/services/id/test/IDServiceJdbcImplTest.java +++ b/services/id/service/src/test/java/org/collectionspace/services/id/test/IDServiceJdbcImplTest.java @@ -1,8 +1,4 @@ /** - * IDServiceJdbcImplTest - * - * Unit tests for the ID Service's JDBC implementation class, IDServiceJdbcImpl. - * * This document is a part of the source code and related artifacts * for CollectionSpace, an open source collections management system * for museums and related institutions: @@ -17,10 +13,6 @@ * * You may obtain a copy of the ECL 2.0 License at * https://source.collectionspace.org/collection-space/LICENSE.txt - * - * $LastChangedBy: aron $ - * $LastChangedRevision: 302 $ - * $LastChangedDate$ */ package org.collectionspace.services.id.test; @@ -29,93 +21,121 @@ import org.collectionspace.services.id.*; import junit.framework.TestCase; import static org.junit.Assert.*; +import org.junit.BeforeClass; +import org.junit.Test; +/** + * IDServiceJdbcImplTest + * + * Unit tests for the ID Service's JDBC implementation class, IDServiceJdbcImpl. + * + * $LastChangedBy: aron $ + * $LastChangedRevision: 302 $ + * $LastChangedDate$ + */ public class IDServiceJdbcImplTest extends TestCase { + // *IMPORTANT* + // @TODO This class is in an early state of a refactoring to + // reflect a change from IDPatterns to IDGenerators at the top level + // of the ID Service. As a result, there will be some naming + // inconsistencies throughout this source file. + String csid; String nextId; - String serializedPattern; + String serializedGenerator; IDPattern pattern; IDServiceJdbcImpl jdbc = new IDServiceJdbcImpl(); IDService service = jdbc; final static String DEFAULT_CSID = "TEST-1"; + + @BeforeClass + public static void setUpOnce() { + // @TODO Check for service preconditions before running tests. + } + @Test public void testPlaceholder() { // Placeholder test to avoid "org.testng.TestNGException: // Failure in JUnit mode ...: could not create/run JUnit test suite" // errors until we add working tests to this class. } - -/* - public void testAddIDPattern() { - jdbc.addIDPattern(DEFAULT_CSID, generateSpectrumEntryNumberTestPattern()); +/* + @Test + public void testAddIDGenerator() { + jdbc.addIDGenerator(DEFAULT_CSID, getSpectrumEntryNumberGenerator()); } - public void testReadIDPattern() { + @Test + public void testReadIDGenerator() { - serializedPattern = jdbc.getIDPattern(DEFAULT_CSID); - pattern = IDPatternSerializer.deserialize(serializedPattern); + serializedGenerator = jdbc.getIDGenerator(DEFAULT_CSID); + pattern = IDPatternSerializer.deserialize(serializedGenerator); assertEquals(DEFAULT_CSID, pattern.getCsid()); } - public void testUpdateIDPattern() { + @Test + public void testUpdateIDGenerator() { final String NEW_DESCRIPTION = "new description"; - serializedPattern = jdbc.getIDPattern(DEFAULT_CSID); - - pattern = IDPatternSerializer.deserialize(serializedPattern); + // Retrieve an existing generator, deserialize it, + // update its contents, serialize it, and write it back. + serializedGenerator = jdbc.getIDGenerator(DEFAULT_CSID); + pattern = IDPatternSerializer.deserialize(serializedGenerator); pattern.setDescription(NEW_DESCRIPTION); - serializedPattern = IDPatternSerializer.serialize(pattern); + serializedGenerator = IDPatternSerializer.serialize(pattern); - jdbc.updateIDPattern(DEFAULT_CSID, serializedPattern); + jdbc.updateIDGenerator(DEFAULT_CSID, serializedGenerator); - serializedPattern = jdbc.getIDPattern(DEFAULT_CSID); - pattern = IDPatternSerializer.deserialize(serializedPattern); + serializedGenerator = jdbc.getIDGenerator(DEFAULT_CSID); + pattern = IDPatternSerializer.deserialize(serializedGenerator); assertEquals(NEW_DESCRIPTION, pattern.getDescription()); } - public void testDeleteIDPattern() { - jdbc.deleteIDPattern(DEFAULT_CSID); + @Test + public void testDeleteIDGenerator() { + jdbc.deleteIDGenerator(DEFAULT_CSID); } - public void testNextIDValidPattern() { + @Test + public void testNewIDValidPattern() { csid = DEFAULT_CSID; try { - jdbc.deleteIDPattern(csid); + jdbc.deleteIDGenerator(csid); } catch (Exception e) { // do nothing } - jdbc.addIDPattern(csid, generateSpectrumEntryNumberTestPattern()); + jdbc.addIDGenerator(csid, getSpectrumEntryNumberGenerator()); - assertEquals("E1", service.nextID("TEST-1")); - assertEquals("E2", service.nextID("TEST-1")); - assertEquals("E3", service.nextID("TEST-1")); + assertEquals("E1", service.newID("TEST-1")); + assertEquals("E2", service.newID("TEST-1")); + assertEquals("E3", service.newID("TEST-1")); try { - jdbc.deleteIDPattern(csid); + jdbc.deleteIDGenerator(csid); } catch (Exception e) { // do nothing } - jdbc.addIDPattern(csid, generateChinAccessionNumberTestPattern()); + jdbc.addIDGenerator(csid, getChinAccessionNumberGenerator()); String currentYear = YearIDGenerator.getCurrentYear(); - assertEquals(currentYear + ".1.1", service.nextID("TEST-1")); - assertEquals(currentYear + ".1.2", service.nextID("TEST-1")); - assertEquals(currentYear + ".1.3", service.nextID("TEST-1")); + assertEquals(currentYear + ".1.1", service.newID("TEST-1")); + assertEquals(currentYear + ".1.2", service.newID("TEST-1")); + assertEquals(currentYear + ".1.3", service.newID("TEST-1")); try { - jdbc.deleteIDPattern(csid); + jdbc.deleteIDGenerator(csid); } catch (Exception e) { // do nothing } @@ -126,25 +146,25 @@ public class IDServiceJdbcImplTest extends TestCase { // 1. The ID Service is running and accessible to this test; and // 2. There is no ID pattern retrievable through that service // with the identifier 'non-existent identifier'. + @Test public void testNextIDInvalidPattern() { try { - nextId = service.nextID("non-existent identifier"); + nextId = service.newID("non-existent identifier"); fail("Should have thrown IllegalArgumentException here"); } catch (IllegalArgumentException expected) { // This Exception should be thrown, and thus the test should pass. } } - -*/ // --------------------------------------------------------------- // Utility methods used by tests above // --------------------------------------------------------------- // @TODO Read test patterns from external configuration. - public String generateSpectrumEntryNumberTestPattern() { + + public String getSpectrumEntryNumberGenerator() { pattern = new IDPattern(DEFAULT_CSID); pattern.setDescription("SPECTRUM entry number pattern"); @@ -156,8 +176,7 @@ public class IDServiceJdbcImplTest extends TestCase { } - // @TODO Read test patterns from external configuration. - public String generateChinAccessionNumberTestPattern() { + public String getChinAccessionNumberGenerator() { pattern = new IDPattern(DEFAULT_CSID); pattern.setDescription("CHIN accession number pattern, for items without parts"); @@ -171,5 +190,6 @@ public class IDServiceJdbcImplTest extends TestCase { return IDPatternSerializer.serialize(pattern); } - +*/ + }