RYA-84 Add Mongo Support to Admin Table Implemented admin table interfaces with MongoDB.
Added Test coverage for the Mongo object to RyaDetails adapter and integration tests for the repo. Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/ec598be4 Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/ec598be4 Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/ec598be4 Branch: refs/heads/develop Commit: ec598be4e457ea5782920480dc6dc035947bba04 Parents: 9bdbbf5 Author: isper3at <[email protected]> Authored: Wed Jun 8 02:37:20 2016 -0400 Committer: Aaron Mihalik <[email protected]> Committed: Tue Aug 23 10:41:37 2016 -0400 ---------------------------------------------------------------------- .../java/mvm/rya/api/instance/RyaDetails.java | 25 +- .../mongodb/instance/MongoDetailsAdapter.java | 175 ++++++++++ .../MongoRyaInstanceDetailsRepository.java | 125 +++++++ .../instance/MongoDetailsAdapterTest.java | 230 +++++++++++++ .../instance/MongoRyaDetailsRepositoryIT.java | 325 +++++++++++++++++++ 5 files changed, 871 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/ec598be4/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetails.java ---------------------------------------------------------------------- diff --git a/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetails.java b/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetails.java index 895eb36..9130144 100644 --- a/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetails.java +++ b/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetails.java @@ -220,15 +220,15 @@ public class RyaDetails implements Serializable { public Builder(final RyaDetails details) { requireNonNull(details); - this.instanceName = details.instanceName; - this.version = details.version; - this.entityCentricDetails = details.entityCentricDetails; - this.geoDetails = details.geoDetails; - this.pcjDetails = details.pcjDetails; - this.temporalDetails = details.temporalDetails; - this.freeTextDetails = details.freeTextDetails; - this.prospectorDetails = details.prospectorDetails; - this.joinSelectivityDetails = details.joinSelectivityDetails; + instanceName = details.instanceName; + version = details.version; + entityCentricDetails = details.entityCentricDetails; + geoDetails = details.geoDetails; + pcjDetails = details.pcjDetails; + temporalDetails = details.temporalDetails; + freeTextDetails = details.freeTextDetails; + prospectorDetails = details.prospectorDetails; + joinSelectivityDetails = details.joinSelectivityDetails; } /** @@ -658,6 +658,13 @@ public class RyaDetails implements Serializable { this.updateAppName = requireNonNull(updateAppName); } + /** + * @return The name of the Fluo application. + */ + public String getUpdateAppName() { + return updateAppName; + } + @Override public int hashCode() { return Objects.hash(updateAppName); http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/ec598be4/dao/mongodb.rya/src/main/java/mvm/rya/mongodb/instance/MongoDetailsAdapter.java ---------------------------------------------------------------------- diff --git a/dao/mongodb.rya/src/main/java/mvm/rya/mongodb/instance/MongoDetailsAdapter.java b/dao/mongodb.rya/src/main/java/mvm/rya/mongodb/instance/MongoDetailsAdapter.java new file mode 100644 index 0000000..ffc4ab1 --- /dev/null +++ b/dao/mongodb.rya/src/main/java/mvm/rya/mongodb/instance/MongoDetailsAdapter.java @@ -0,0 +1,175 @@ +package mvm.rya.mongodb.instance; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.BasicDBObjectBuilder; +import com.mongodb.DBObject; + +import mvm.rya.api.instance.RyaDetails; +import mvm.rya.api.instance.RyaDetails.EntityCentricIndexDetails; +import mvm.rya.api.instance.RyaDetails.FreeTextIndexDetails; +import mvm.rya.api.instance.RyaDetails.GeoIndexDetails; +import mvm.rya.api.instance.RyaDetails.JoinSelectivityDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.FluoDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails.PCJUpdateStrategy; +import mvm.rya.api.instance.RyaDetails.ProspectorDetails; +import mvm.rya.api.instance.RyaDetails.TemporalIndexDetails; + +/** + * Serializes configuration details for use in Mongo. + * The {@link DBObject} will look like: + * <pre> + * {@code + * { + * "instanceName": <string>, + * "version": <string>?, + * "entityCentricDetails": <boolean>, + * "geoDetails": <boolean>, + * "pcjDetails": { + * "enabled": <boolean>, + * "fluoName": <string>, + * "pcjs": [{ + * "id": <string>, + * "updateStrategy": <string>, + * "lastUpdate": <date> + * },...,{} + * ] + * }, + * "temporalDetails": <boolean>, + * "freeTextDetails": <boolean>, + * "prospectorDetails": <date>, + * "joinSelectivityDetails": <date> + * } + * </pre> + */ +@ParametersAreNonnullByDefault +public class MongoDetailsAdapter { + public static final String INSTANCE_KEY = "instanceName"; + public static final String VERSION_KEY = "version"; + + public static final String ENTITY_DETAILS_KEY = "entityCentricDetails"; + public static final String GEO_DETAILS_KEY = "geoDetails"; + public static final String PCJ_DETAILS_KEY = "pcjDetails"; + public static final String PCJ_ENABLED_KEY = "enabled"; + public static final String PCJ_FLUO_KEY = "fluoName"; + public static final String PCJ_PCJS_KEY = "pcjs"; + public static final String PCJ_ID_KEY = "id"; + public static final String PCJ_UPDATE_STRAT_KEY = "updateStrategy"; + public static final String PCJ_LAST_UPDATE_KEY = "lastUpdate"; + public static final String TEMPORAL_DETAILS_KEY = "temporalDetails"; + public static final String FREETEXT_DETAILS_KEY = "freeTextDetails"; + + public static final String PROSPECTOR_DETAILS_KEY = "prospectorDetails"; + public static final String JOIN_SELECTIVITY_DETAILS_KEY = "joinSelectivitiyDetails"; + + /** + * Serializes {@link RyaDetails} to mongo {@link DBObject}. + * @param details - The details to be serialized. + * @return The mongo {@link DBObject}. + */ + public static BasicDBObject toDBObject(final RyaDetails details) { + Preconditions.checkNotNull(details); + final BasicDBObjectBuilder builder = BasicDBObjectBuilder.start() + .add(INSTANCE_KEY, details.getRyaInstanceName()) + .add(VERSION_KEY, details.getRyaVersion()) + .add(ENTITY_DETAILS_KEY, details.getEntityCentricIndexDetails().isEnabled()) + .add(GEO_DETAILS_KEY, details.getGeoIndexDetails().isEnabled()) + .add(PCJ_DETAILS_KEY, getPCJDetailsDBObject(details)) + .add(TEMPORAL_DETAILS_KEY, details.getTemporalIndexDetails().isEnabled()) + .add(FREETEXT_DETAILS_KEY, details.getFreeTextIndexDetails().isEnabled()); + if(details.getProspectorDetails().getLastUpdated().isPresent()) { + builder.add(PROSPECTOR_DETAILS_KEY, details.getProspectorDetails().getLastUpdated().get()); + } + if(details.getJoinSelectivityDetails().getLastUpdated().isPresent()) { + builder.add(JOIN_SELECTIVITY_DETAILS_KEY, details.getJoinSelectivityDetails().getLastUpdated().get()); + } + return (BasicDBObject) builder.get(); + } + + private static DBObject getPCJDetailsDBObject(final RyaDetails details) { + final PCJIndexDetails pcjDetails = details.getPCJIndexDetails(); + final BasicDBObjectBuilder pcjBuilder = BasicDBObjectBuilder.start() + .add(PCJ_ENABLED_KEY, pcjDetails.isEnabled()); + if(pcjDetails.getFluoDetails().isPresent()) { + pcjBuilder.add(PCJ_FLUO_KEY, pcjDetails.getFluoDetails().get().getUpdateAppName()); + } + final List<DBObject> pcjDetailList = new ArrayList<>(); + for(final PCJDetails pcjDetail : pcjDetails.getPCJDetails()) { + final BasicDBObjectBuilder indBuilbder = BasicDBObjectBuilder.start() + .add(PCJ_ID_KEY, pcjDetail.getId()) + .add(PCJ_UPDATE_STRAT_KEY, pcjDetail.getUpdateStrategy().name()); + if(pcjDetail.getLastUpdateTime().isPresent()) { + indBuilbder.add(PCJ_LAST_UPDATE_KEY, pcjDetail.getLastUpdateTime().get()); + } + pcjDetailList.add(indBuilbder.get()); + } + pcjBuilder.add(PCJ_PCJS_KEY, pcjDetailList.toArray()); + return pcjBuilder.get(); + } + + public static RyaDetails toRyaDetails(final DBObject mongoObj) throws MalformedRyaDetailsException { + final BasicDBObject basicObj = (BasicDBObject) mongoObj; + try { + return RyaDetails.builder() + .setRyaInstanceName(basicObj.getString(INSTANCE_KEY)) + .setRyaVersion(basicObj.getString(VERSION_KEY)) + .setEntityCentricIndexDetails(new EntityCentricIndexDetails(basicObj.getBoolean(ENTITY_DETAILS_KEY))) + .setGeoIndexDetails(new GeoIndexDetails(basicObj.getBoolean(GEO_DETAILS_KEY))) + .setPCJIndexDetails(getPCJDetails(basicObj)) + .setTemporalIndexDetails(new TemporalIndexDetails(basicObj.getBoolean(TEMPORAL_DETAILS_KEY))) + .setFreeTextDetails(new FreeTextIndexDetails(basicObj.getBoolean(FREETEXT_DETAILS_KEY))) + .setProspectorDetails(new ProspectorDetails(Optional.<Date>fromNullable(basicObj.getDate(PROSPECTOR_DETAILS_KEY)))) + .setJoinSelectivityDetails(new JoinSelectivityDetails(Optional.<Date>fromNullable(basicObj.getDate(JOIN_SELECTIVITY_DETAILS_KEY)))) + .build(); + } catch(final Exception e) { + throw new MalformedRyaDetailsException("Failed to make RyaDetail from Mongo Object, it is malformed.", e); + } + } + + private static PCJIndexDetails getPCJDetails(final BasicDBObject basicObj) { + final BasicDBObject pcjObj = (BasicDBObject) basicObj.get(PCJ_DETAILS_KEY); + final PCJIndexDetails.Builder pcjBuilder = PCJIndexDetails.builder() + .setEnabled(pcjObj.getBoolean(PCJ_ENABLED_KEY)) + .setFluoDetails(new FluoDetails(pcjObj.getString(PCJ_FLUO_KEY))); + final BasicDBList pcjs = (BasicDBList) pcjObj.get(PCJ_PCJS_KEY); + if(pcjs != null) { + for(int ii = 0; ii < pcjs.size(); ii++) { + final BasicDBObject pcj = (BasicDBObject) pcjs.get(ii); + pcjBuilder.addPCJDetails( + PCJDetails.builder() + .setId(pcj.getString(PCJ_ID_KEY)) + .setUpdateStrategy(PCJUpdateStrategy.valueOf((String)pcj.get(PCJ_UPDATE_STRAT_KEY))) + .setLastUpdateTime(pcj.getDate(PCJ_LAST_UPDATE_KEY)) + .build()); + } + } + return pcjBuilder.build(); + } + + /** + * Exception thrown when a MongoDB {@link DBObject} is malformed when attemptin + * to adapt it into a {@link RyaDetails}. + */ + public static class MalformedRyaDetailsException extends Exception { + private static final long serialVersionUID = 1L; + + /** + * Creates a new {@link MalformedRyaDetailsException} + * @param message - The message to be displayed by the exception. + * @param e - The source cause of the exception. + */ + public MalformedRyaDetailsException(final String message, final Throwable e) { + super(message, e); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/ec598be4/dao/mongodb.rya/src/main/java/mvm/rya/mongodb/instance/MongoRyaInstanceDetailsRepository.java ---------------------------------------------------------------------- diff --git a/dao/mongodb.rya/src/main/java/mvm/rya/mongodb/instance/MongoRyaInstanceDetailsRepository.java b/dao/mongodb.rya/src/main/java/mvm/rya/mongodb/instance/MongoRyaInstanceDetailsRepository.java new file mode 100644 index 0000000..51ba9c2 --- /dev/null +++ b/dao/mongodb.rya/src/main/java/mvm/rya/mongodb/instance/MongoRyaInstanceDetailsRepository.java @@ -0,0 +1,125 @@ +package mvm.rya.mongodb.instance; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; + +import javax.annotation.ParametersAreNonnullByDefault; + +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; +import com.mongodb.MongoClient; +import com.mongodb.WriteResult; + +import mvm.rya.api.instance.RyaDetails; +import mvm.rya.api.instance.RyaDetailsRepository; +import mvm.rya.mongodb.instance.MongoDetailsAdapter.MalformedRyaDetailsException; + +/** + * An implementation of {@link RyaDetailsRepository} that stores a Rya + * instance's {@link RyaDetails} in a Mongo document. + */ +@ParametersAreNonnullByDefault +public class MongoRyaInstanceDetailsRepository implements RyaDetailsRepository { + private static final String INSTANCE_DETAILS_COLLECTION_NAME = "instance_details"; + + private final DB db; + private final String instanceName; + + /** + * Constructs an instance of {@link MongoRyaInstanceDetailsRepository}. + * + * @param client - Connects to the instance of Mongo that hosts the Rya instance. (not null) + * @param instanceName - The name of the Rya instance this repository represents. (not null) + */ + public MongoRyaInstanceDetailsRepository(final MongoClient client, final String instanceName) { + checkNotNull(client); + this.instanceName = requireNonNull( instanceName ); + db = client.getDB(this.instanceName); + } + + @Override + public boolean isInitialized() throws RyaDetailsRepositoryException { + final DBCollection col = db.getCollection(INSTANCE_DETAILS_COLLECTION_NAME); + return col.count() == 1; + } + + @Override + public void initialize(final RyaDetails details) throws AlreadyInitializedException, RyaDetailsRepositoryException { + // Preconditions. + requireNonNull( details ); + + if(!details.getRyaInstanceName().equals( instanceName )) { + throw new RyaDetailsRepositoryException("The instance name that was in the provided 'details' does not match " + + "the instance name that this repository is connected to. Make sure you're connected to the" + + "correct Rya instance."); + } + + if(isInitialized()) { + throw new AlreadyInitializedException("The repository has already been initialized for the Rya instance named '" + + instanceName + "'."); + } + + // Create the document that hosts the details if it has not been created yet. + final DBCollection col = db.createCollection(INSTANCE_DETAILS_COLLECTION_NAME, new BasicDBObject()); + + // Write the details to the collection. + col.insert(MongoDetailsAdapter.toDBObject(details)); + } + + @Override + public RyaDetails getRyaInstanceDetails() throws NotInitializedException, RyaDetailsRepositoryException { + // Preconditions. + if(!isInitialized()) { + throw new NotInitializedException("Could not fetch the details for the Rya instanced named '" + + instanceName + "' because it has not been initialized yet."); + } + + // Fetch the value from the collection. + final DBCollection col = db.getCollection(INSTANCE_DETAILS_COLLECTION_NAME); + //There should only be one document in the collection. + final DBObject mongoObj = col.findOne(); + + try{ + // Deserialize it. + return MongoDetailsAdapter.toRyaDetails( mongoObj ); + } catch (final MalformedRyaDetailsException e) { + throw new RyaDetailsRepositoryException("The existing details details are malformed.", e); + } + } + + @Override + public void update(final RyaDetails oldDetails, final RyaDetails newDetails) + throws NotInitializedException, ConcurrentUpdateException, RyaDetailsRepositoryException { + // Preconditions. + requireNonNull(oldDetails); + requireNonNull(newDetails); + + if(!newDetails.getRyaInstanceName().equals( instanceName )) { + throw new RyaDetailsRepositoryException("The instance name that was in the provided 'newDetails' does not match " + + "the instance name that this repository is connected to. Make sure you're connected to the" + + "correct Rya instance."); + } + + if(!isInitialized()) { + throw new NotInitializedException("Could not update the details for the Rya instanced named '" + + instanceName + "' because it has not been initialized yet."); + } + + if(oldDetails.equals(newDetails)) { + return; + } + + final DBCollection col = db.getCollection(INSTANCE_DETAILS_COLLECTION_NAME); + final DBObject oldObj = MongoDetailsAdapter.toDBObject(oldDetails); + final DBObject newObj = MongoDetailsAdapter.toDBObject(newDetails); + final WriteResult result = col.update(oldObj, newObj); + + //since there is only 1 document, there should only be 1 update. + if(result.getN() != 1) { + throw new ConcurrentUpdateException("Could not update the details for the Rya instance named '" + + instanceName + "' because the old value is out of date."); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/ec598be4/dao/mongodb.rya/src/test/java/mvm/rya/mongodb/instance/MongoDetailsAdapterTest.java ---------------------------------------------------------------------- diff --git a/dao/mongodb.rya/src/test/java/mvm/rya/mongodb/instance/MongoDetailsAdapterTest.java b/dao/mongodb.rya/src/test/java/mvm/rya/mongodb/instance/MongoDetailsAdapterTest.java new file mode 100644 index 0000000..8628dbd --- /dev/null +++ b/dao/mongodb.rya/src/test/java/mvm/rya/mongodb/instance/MongoDetailsAdapterTest.java @@ -0,0 +1,230 @@ +package mvm.rya.mongodb.instance; + +import static org.junit.Assert.assertEquals; + +import java.util.Date; + +import org.junit.Test; + +import com.google.common.base.Optional; +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; +import com.mongodb.util.JSON; + +import mvm.rya.api.instance.RyaDetails; +import mvm.rya.api.instance.RyaDetails.EntityCentricIndexDetails; +import mvm.rya.api.instance.RyaDetails.FreeTextIndexDetails; +import mvm.rya.api.instance.RyaDetails.GeoIndexDetails; +import mvm.rya.api.instance.RyaDetails.JoinSelectivityDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.FluoDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails.PCJUpdateStrategy; +import mvm.rya.api.instance.RyaDetails.ProspectorDetails; +import mvm.rya.api.instance.RyaDetails.TemporalIndexDetails; +import mvm.rya.mongodb.instance.MongoDetailsAdapter.MalformedRyaDetailsException; + +public class MongoDetailsAdapterTest { + @Test + public void ryaDetailsToMongoTest() { + final PCJIndexDetails.Builder pcjBuilder = PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails(new FluoDetails("fluo")); + for(int ii = 0; ii < 2; ii++) { + pcjBuilder.addPCJDetails( + PCJDetails.builder() + .setId("pcj_"+ii) + .setUpdateStrategy(PCJUpdateStrategy.BATCH) + .setLastUpdateTime(new Date(ii)) + .build()); + } + + final RyaDetails details = RyaDetails.builder() + .setRyaInstanceName("test") + .setRyaVersion("1") + .setEntityCentricIndexDetails(new EntityCentricIndexDetails(true)) + .setGeoIndexDetails(new GeoIndexDetails(true)) + .setPCJIndexDetails(pcjBuilder.build()) + .setTemporalIndexDetails(new TemporalIndexDetails(true)) + .setFreeTextDetails(new FreeTextIndexDetails(true)) + .setProspectorDetails(new ProspectorDetails(Optional.<Date>fromNullable(new Date(0L)))) + .setJoinSelectivityDetails(new JoinSelectivityDetails(Optional.<Date>fromNullable(new Date(1L)))) + .build(); + + final DBObject expected = (DBObject) JSON.parse( + "{ " + + "instanceName : \"test\"," + + "version : \"1\"," + + "entityCentricDetails : true," + + "geoDetails : true," + + "pcjDetails : {" + + "enabled : true ," + + "fluoName : \"fluo\"," + + "pcjs : [ " + + "{" + + "id : \"pcj_0\"," + + "updateStrategy : \"BATCH\"," + + "lastUpdate : { $date : \"1970-01-01T00:00:00.000Z\"}" + + "}," + + "{" + + "id : \"pcj_1\"," + + "updateStrategy : \"BATCH\"," + + "lastUpdate : { $date : \"1970-01-01T00:00:00.001Z\"}" + + "}]" + + "}," + + "temporalDetails : true," + + "freeTextDetails : true," + + "prospectorDetails : { $date : \"1970-01-01T00:00:00.000Z\"}," + + "joinSelectivitiyDetails : { $date : \"1970-01-01T00:00:00.001Z\"}" + + "}" + ); + final BasicDBObject actual = MongoDetailsAdapter.toDBObject(details); + System.out.println(expected.toString()); + System.out.println("***"); + System.out.println(actual.toString()); + assertEquals(expected.toString(), actual.toString()); + } + + @Test + public void mongoToRyaDetailsTest() throws MalformedRyaDetailsException { + final BasicDBObject mongo = (BasicDBObject) JSON.parse( + "{ " + + "instanceName : \"test\"," + + "version : \"1\"," + + "entityCentricDetails : true," + + "geoDetails : true," + + "pcjDetails : {" + + "enabled : true ," + + "fluoName : \"fluo\"," + + "pcjs : [ " + + "{" + + "id : \"pcj_0\"," + + "updateStrategy : \"BATCH\"," + + "lastUpdate : { $date : \"1970-01-01T00:00:00.000Z\"}" + + "}," + + "{" + + "id : \"pcj_1\"," + + "updateStrategy : \"BATCH\"," + + "lastUpdate : { $date : \"1970-01-01T00:00:00.001Z\"}" + + "}]" + + "}," + + "temporalDetails : true," + + "freeTextDetails : true," + + "prospectorDetails : { $date : \"1970-01-01T00:00:00.000Z\"}," + + "joinSelectivitiyDetails : { $date : \"1970-01-01T00:00:00.001Z\"}" + + "}" + ); + final PCJIndexDetails.Builder pcjBuilder = PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails(new FluoDetails("fluo")); + for(int ii = 0; ii < 2; ii++) { + pcjBuilder.addPCJDetails( + PCJDetails.builder() + .setId("pcj_"+ii) + .setUpdateStrategy(PCJUpdateStrategy.BATCH) + .setLastUpdateTime(new Date(ii)) + .build()); + } + + final RyaDetails expected = RyaDetails.builder() + .setRyaInstanceName("test") + .setRyaVersion("1") + .setEntityCentricIndexDetails(new EntityCentricIndexDetails(true)) + .setGeoIndexDetails(new GeoIndexDetails(true)) + .setPCJIndexDetails(pcjBuilder.build()) + .setTemporalIndexDetails(new TemporalIndexDetails(true)) + .setFreeTextDetails(new FreeTextIndexDetails(true)) + .setProspectorDetails(new ProspectorDetails(Optional.<Date>fromNullable(new Date(0L)))) + .setJoinSelectivityDetails(new JoinSelectivityDetails(Optional.<Date>fromNullable(new Date(1L)))) + .build(); + + final RyaDetails actual = MongoDetailsAdapter.toRyaDetails(mongo); + assertEquals(expected, actual); + } + + @Test + public void absentOptionalToRyaDetailsTest() throws MalformedRyaDetailsException { + final BasicDBObject mongo = (BasicDBObject) JSON.parse( + "{ " + + "instanceName : \"test\"," + + "version : \"1\"," + + "entityCentricDetails : true," + + "geoDetails : false," + + "pcjDetails : {" + + "enabled : false," + + "fluoName : \"fluo\"," + + "pcjs : [ " + + "{" + + "id : \"pcj_1\"," + + "updateStrategy : \"INCREMENTAL\"" + + "}" + + "]" + + "}," + + "temporalDetails : false," + + "freeTextDetails : true," + + "prospectorDetails : null," + + "joinSelectivitiyDetails : null" + + "}" + ); + final PCJIndexDetails.Builder pcjBuilder = PCJIndexDetails.builder() + .setEnabled(false) + .setFluoDetails(new FluoDetails("fluo")) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj_1") + .setUpdateStrategy(PCJUpdateStrategy.INCREMENTAL) + .setLastUpdateTime(null).build()); + + final RyaDetails expected = RyaDetails.builder() + .setRyaInstanceName("test") + .setRyaVersion("1") + .setEntityCentricIndexDetails(new EntityCentricIndexDetails(true)) + .setGeoIndexDetails(new GeoIndexDetails(false)) + .setPCJIndexDetails(pcjBuilder.build()) + .setTemporalIndexDetails(new TemporalIndexDetails(false)) + .setFreeTextDetails(new FreeTextIndexDetails(true)) + .setProspectorDetails(new ProspectorDetails(Optional.<Date>absent())) + .setJoinSelectivityDetails(new JoinSelectivityDetails(Optional.<Date>absent())) + .build(); + + final RyaDetails actual = MongoDetailsAdapter.toRyaDetails(mongo); + assertEquals(expected, actual); + } + + @Test + public void absentOptionalToMongoTest() { + final BasicDBObject expected = (BasicDBObject) JSON.parse( + "{ " + + "instanceName : \"test\"," + + "version : \"1\"," + + "entityCentricDetails : true," + + "geoDetails : false," + + "pcjDetails : {" + + "enabled : true," + + "fluoName : \"fluo\"," + + "pcjs : [ ]" + + "}," + + "temporalDetails : false," + + "freeTextDetails : true" + + "}" + ); + final PCJIndexDetails.Builder pcjBuilder = PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails(new FluoDetails("fluo")); + + final RyaDetails details = RyaDetails.builder() + .setRyaInstanceName("test") + .setRyaVersion("1") + .setEntityCentricIndexDetails(new EntityCentricIndexDetails(true)) + .setGeoIndexDetails(new GeoIndexDetails(false)) + .setPCJIndexDetails(pcjBuilder.build()) + .setTemporalIndexDetails(new TemporalIndexDetails(false)) + .setFreeTextDetails(new FreeTextIndexDetails(true)) + .setProspectorDetails(new ProspectorDetails(Optional.<Date>absent())) + .setJoinSelectivityDetails(new JoinSelectivityDetails(Optional.<Date>absent())) + .build(); + + final DBObject actual = MongoDetailsAdapter.toDBObject(details); + assertEquals(expected, actual); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/ec598be4/dao/mongodb.rya/src/test/java/mvm/rya/mongodb/instance/MongoRyaDetailsRepositoryIT.java ---------------------------------------------------------------------- diff --git a/dao/mongodb.rya/src/test/java/mvm/rya/mongodb/instance/MongoRyaDetailsRepositoryIT.java b/dao/mongodb.rya/src/test/java/mvm/rya/mongodb/instance/MongoRyaDetailsRepositoryIT.java new file mode 100644 index 0000000..05dab7e --- /dev/null +++ b/dao/mongodb.rya/src/test/java/mvm/rya/mongodb/instance/MongoRyaDetailsRepositoryIT.java @@ -0,0 +1,325 @@ +package mvm.rya.mongodb.instance; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Date; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.common.base.Optional; +import com.mongodb.DB; +import com.mongodb.MongoClient; +import com.mongodb.MongoException; + +import de.flapdoodle.embed.mongo.tests.MongodForTestsFactory; +import mvm.rya.api.instance.RyaDetails; +import mvm.rya.api.instance.RyaDetails.EntityCentricIndexDetails; +import mvm.rya.api.instance.RyaDetails.FreeTextIndexDetails; +import mvm.rya.api.instance.RyaDetails.GeoIndexDetails; +import mvm.rya.api.instance.RyaDetails.JoinSelectivityDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.FluoDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails; +import mvm.rya.api.instance.RyaDetails.PCJIndexDetails.PCJDetails.PCJUpdateStrategy; +import mvm.rya.api.instance.RyaDetails.ProspectorDetails; +import mvm.rya.api.instance.RyaDetails.TemporalIndexDetails; +import mvm.rya.api.instance.RyaDetailsRepository; +import mvm.rya.api.instance.RyaDetailsRepository.AlreadyInitializedException; +import mvm.rya.api.instance.RyaDetailsRepository.ConcurrentUpdateException; +import mvm.rya.api.instance.RyaDetailsRepository.NotInitializedException; +import mvm.rya.api.instance.RyaDetailsRepository.RyaDetailsRepositoryException; + +/** + * Tests the methods of {@link AccumuloRyaDetailsRepository} by using a {@link MiniAccumuloCluster}. + */ +public class MongoRyaDetailsRepositoryIT { + + private static MongoClient client = null; + private static DB testDB = null; + @BeforeClass + public static void startMiniAccumulo() throws MongoException, IOException { + final MongodForTestsFactory mongoFactory = new MongodForTestsFactory(); + client = mongoFactory.newMongo(); + } + + @Before + public void clearLastTest() { + client.dropDatabase("testInstance"); + testDB = client.getDB("testInstance"); + } + + @AfterClass + public static void stopMiniAccumulo() throws IOException, InterruptedException { + client.close(); + } + + @Test + public void initializeAndGet() throws AlreadyInitializedException, RyaDetailsRepositoryException { + final String instanceName = "testInstance"; + + // Create the metadata object the repository will be initialized with. + final RyaDetails details = RyaDetails.builder() + .setRyaInstanceName(instanceName) + .setRyaVersion("1.2.3.4") + .setEntityCentricIndexDetails( new EntityCentricIndexDetails(true) ) + .setGeoIndexDetails( new GeoIndexDetails(true) ) + .setTemporalIndexDetails( new TemporalIndexDetails(true) ) + .setFreeTextDetails( new FreeTextIndexDetails(true) ) + .setPCJIndexDetails( + PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails( new FluoDetails("test_instance_rya_pcj_updater") ) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 1") + .setUpdateStrategy(PCJUpdateStrategy.BATCH) + .setLastUpdateTime( new Date() ) + .build()) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 2") + .setUpdateStrategy(PCJUpdateStrategy.INCREMENTAL) + .build()) + .build()) + .setProspectorDetails( new ProspectorDetails(Optional.of(new Date())) ) + .setJoinSelectivityDetails( new JoinSelectivityDetails(Optional.of(new Date())) ) + .build(); + + // Setup the repository that will be tested using a mock instance of MongoDB. + final RyaDetailsRepository repo = new MongoRyaInstanceDetailsRepository(client, instanceName); + + // Initialize the repository + repo.initialize(details); + + // Fetch the stored details. + final RyaDetails stored = repo.getRyaInstanceDetails(); + + // Ensure the fetched object is equivalent to what was stored. + assertEquals(details, stored); + } + + @Test(expected = AlreadyInitializedException.class) + public void initialize_alreadyInitialized() throws AlreadyInitializedException, RyaDetailsRepositoryException { + final String instanceName = "testInstance"; + + // Create the metadata object the repository will be initialized with. + final RyaDetails details = RyaDetails.builder() + .setRyaInstanceName(instanceName) + .setRyaVersion("1.2.3.4") + .setEntityCentricIndexDetails( new EntityCentricIndexDetails(true) ) + .setGeoIndexDetails( new GeoIndexDetails(true) ) + .setTemporalIndexDetails( new TemporalIndexDetails(true) ) + .setFreeTextDetails( new FreeTextIndexDetails(true) ) + .setPCJIndexDetails( + PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails( new FluoDetails("test_instance_rya_pcj_updater") ) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 1") + .setUpdateStrategy(PCJUpdateStrategy.BATCH) + .setLastUpdateTime( new Date() ) + .build()) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 2") + .setUpdateStrategy(PCJUpdateStrategy.INCREMENTAL) + .build()) + .build()) + .setProspectorDetails( new ProspectorDetails(Optional.of(new Date())) ) + .setJoinSelectivityDetails( new JoinSelectivityDetails(Optional.of(new Date())) ) + .build(); + + // Setup the repository that will be tested using a mock instance of MongoDB. + final RyaDetailsRepository repo = new MongoRyaInstanceDetailsRepository(client, instanceName); + + // Initialize the repository + repo.initialize(details); + + // Initialize it again. + repo.initialize(details); + } + + @Test(expected = NotInitializedException.class) + public void getRyaInstance_notInitialized() throws NotInitializedException, RyaDetailsRepositoryException { + // Setup the repository that will be tested using a mock instance of Accumulo. + final RyaDetailsRepository repo = new MongoRyaInstanceDetailsRepository(client, "testInstance"); + + // Try to fetch the details from the uninitialized repository. + repo.getRyaInstanceDetails(); + } + + @Test + public void isInitialized_true() throws AlreadyInitializedException, RyaDetailsRepositoryException { + final String instanceName = "testInstance"; + + // Create the metadata object the repository will be initialized with. + final RyaDetails details = RyaDetails.builder() + .setRyaInstanceName(instanceName) + .setRyaVersion("1.2.3.4") + .setEntityCentricIndexDetails( new EntityCentricIndexDetails(true) ) + .setGeoIndexDetails( new GeoIndexDetails(true) ) + .setTemporalIndexDetails( new TemporalIndexDetails(true) ) + .setFreeTextDetails( new FreeTextIndexDetails(true) ) + .setPCJIndexDetails( + PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails( new FluoDetails("test_instance_rya_pcj_updater") ) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 1") + .setUpdateStrategy(PCJUpdateStrategy.BATCH) + .setLastUpdateTime( new Date() ) + .build()) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 2") + .setUpdateStrategy(PCJUpdateStrategy.INCREMENTAL) + .build()) + .build()) + .setProspectorDetails( new ProspectorDetails(Optional.of(new Date())) ) + .setJoinSelectivityDetails( new JoinSelectivityDetails(Optional.of(new Date())) ) + .build(); + + // Setup the repository that will be tested using a mock instance of MongoDB. + final RyaDetailsRepository repo = new MongoRyaInstanceDetailsRepository(client, "testInstance"); + + // Initialize the repository + repo.initialize(details); + + // Ensure the repository reports that it has been initialized. + assertTrue( repo.isInitialized() ); + } + + @Test + public void isInitialized_false() throws RyaDetailsRepositoryException { + // Setup the repository that will be tested using a mock instance of MongoDB. + final RyaDetailsRepository repo = new MongoRyaInstanceDetailsRepository(client, "testInstance"); + + // Ensure the repository reports that is has not been initialized. + assertFalse( repo.isInitialized() ); + } + + @Test + public void update() throws AlreadyInitializedException, RyaDetailsRepositoryException { + final String instanceName = "testInstance"; + + // Create the metadata object the repository will be initialized with. + final RyaDetails details = RyaDetails.builder() + .setRyaInstanceName(instanceName) + .setRyaVersion("1.2.3.4") + .setEntityCentricIndexDetails( new EntityCentricIndexDetails(true) ) + .setGeoIndexDetails( new GeoIndexDetails(true) ) + .setTemporalIndexDetails( new TemporalIndexDetails(true) ) + .setFreeTextDetails( new FreeTextIndexDetails(true) ) + .setPCJIndexDetails( + PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails( new FluoDetails("test_instance_rya_pcj_updater") ) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 1") + .setUpdateStrategy(PCJUpdateStrategy.BATCH) + .setLastUpdateTime( new Date() ) + .build()) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 2") + .setUpdateStrategy(PCJUpdateStrategy.INCREMENTAL) + .build()) + .build()) + .setProspectorDetails( new ProspectorDetails(Optional.of(new Date())) ) + .setJoinSelectivityDetails( new JoinSelectivityDetails(Optional.of(new Date())) ) + .build(); + + // Setup the repository that will be tested using a mock instance of MongoDB. + final RyaDetailsRepository repo = new MongoRyaInstanceDetailsRepository(client, "testInstance"); + + // Initialize the repository + repo.initialize(details); + + // Create a new state for the details. + final RyaDetails updated = new RyaDetails.Builder( details ) + .setGeoIndexDetails( new GeoIndexDetails(false) ) + .build(); + + // Execute the update. + repo.update(details, updated); + + // Show the new state that is stored matches the updated state. + final RyaDetails fetched = repo.getRyaInstanceDetails(); + assertEquals(updated, fetched); + } + + @Test(expected = ConcurrentUpdateException.class) + public void update_outOfDate() throws AlreadyInitializedException, RyaDetailsRepositoryException { + final String instanceName = "testInstance"; + + // Create the metadata object the repository will be initialized with. + final RyaDetails details = RyaDetails.builder() + .setRyaInstanceName(instanceName) + .setRyaVersion("1.2.3.4") + .setEntityCentricIndexDetails( new EntityCentricIndexDetails(true) ) + .setGeoIndexDetails( new GeoIndexDetails(true) ) + .setTemporalIndexDetails( new TemporalIndexDetails(true) ) + .setFreeTextDetails( new FreeTextIndexDetails(true) ) + .setPCJIndexDetails( + PCJIndexDetails.builder() + .setEnabled(true) + .setFluoDetails( new FluoDetails("test_instance_rya_pcj_updater") ) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 1") + .setUpdateStrategy(PCJUpdateStrategy.BATCH) + .setLastUpdateTime( new Date() ) + .build()) + .addPCJDetails( + PCJDetails.builder() + .setId("pcj 2") + .setUpdateStrategy(PCJUpdateStrategy.INCREMENTAL) + .build()) + .build()) + .setProspectorDetails( new ProspectorDetails(Optional.of(new Date())) ) + .setJoinSelectivityDetails( new JoinSelectivityDetails(Optional.of(new Date())) ) + .build(); + + // Setup the repository that will be tested using a mock instance of MongoDB. + final RyaDetailsRepository repo = new MongoRyaInstanceDetailsRepository(client, "testInstance"); + + // Initialize the repository + repo.initialize(details); + + // Create a new state for the details. + final RyaDetails updated = new RyaDetails.Builder( details ) + .setGeoIndexDetails( new GeoIndexDetails(false) ) + .build(); + + // Try to execute the update where the old state is not the currently stored state. + repo.update(updated, updated); + } +} \ No newline at end of file
