You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by mi...@apache.org on 2016/08/23 19:38:45 UTC
[17/34] incubator-rya git commit: RYA-81 Added a structure that
provides metadata about an instance of Rya as well as a repository that may
be used to store and update it. This information could be used to configure
clients when querying the instance.
RYA-81 Added a structure that provides metadata about an instance of Rya as well as a repository that may be used to store and update it. This information could be used to configure clients when querying the instance.
Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/9bdbbf5d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/9bdbbf5d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/9bdbbf5d
Branch: refs/heads/develop
Commit: 9bdbbf5dd9ba5f7662e0044c09a1acf17095b616
Parents: d49aa6d
Author: Kevin Chilton <ke...@parsons.com>
Authored: Sat Jun 4 17:46:43 2016 -0400
Committer: Aaron Mihalik <mi...@alum.mit.edu>
Committed: Tue Aug 23 10:41:37 2016 -0400
----------------------------------------------------------------------
.../java/mvm/rya/api/instance/RyaDetails.java | 908 +++++++++++++++++++
.../rya/api/instance/RyaDetailsRepository.java | 123 +++
.../mvm/rya/api/instance/RyaDetailsTest.java | 149 +++
dao/accumulo.rya/pom.xml | 90 ++
.../AccumuloRyaInstanceDetailsRepository.java | 229 +++++
.../accumulo/instance/RyaDetailsSerializer.java | 96 ++
.../AccumuloRyaDetailsRepositoryIT.java | 368 ++++++++
7 files changed, 1963 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9bdbbf5d/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
new file mode 100644
index 0000000..895eb36
--- /dev/null
+++ b/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetails.java
@@ -0,0 +1,908 @@
+package mvm.rya.api.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 java.util.Objects.requireNonNull;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+import javax.annotation.ParametersAreNonnullByDefault;
+import javax.annotation.concurrent.Immutable;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Details about how a Rya instance's state.
+ */
+@Immutable
+@ParametersAreNonnullByDefault
+public class RyaDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ // General metadata about the instance.
+ private final String instanceName;
+ private final String version;
+
+ // Secondary Index Details.
+ private final EntityCentricIndexDetails entityCentricDetails;
+ private final GeoIndexDetails geoDetails;
+ private final PCJIndexDetails pcjDetails;
+ private final TemporalIndexDetails temporalDetails;
+ private final FreeTextIndexDetails freeTextDetails;
+
+ // Statistics Details.
+ private final ProspectorDetails prospectorDetails;
+ private final JoinSelectivityDetails joinSelectivityDetails;
+
+ /**
+ * Private to prevent initialization through the constructor. To build
+ * instances of this class, use the {@link Builder}.
+ */
+ private RyaDetails(
+ final String instanceName,
+ final String version,
+ final EntityCentricIndexDetails entityCentricDetails,
+ final GeoIndexDetails geoDetails,
+ final PCJIndexDetails pcjDetails,
+ final TemporalIndexDetails temporalDetails,
+ final FreeTextIndexDetails freeTextDetails,
+ final ProspectorDetails prospectorDetails,
+ final JoinSelectivityDetails joinSelectivityDetails) {
+ this.instanceName = requireNonNull(instanceName);
+ this.version = requireNonNull(version);
+ this.entityCentricDetails = requireNonNull(entityCentricDetails);
+ this.geoDetails = requireNonNull(geoDetails);
+ this.pcjDetails = requireNonNull(pcjDetails);
+ this.temporalDetails = requireNonNull(temporalDetails);
+ this.freeTextDetails = requireNonNull(freeTextDetails);
+ this.prospectorDetails = requireNonNull(prospectorDetails);
+ this.joinSelectivityDetails = requireNonNull(joinSelectivityDetails);
+ }
+
+ /**
+ * @return The name that uniquely identifies the instance of Rya within
+ * the system that hosts it.
+ */
+ public String getRyaInstanceName() {
+ return instanceName;
+ }
+
+ /**
+ * @return The version of Rya this instance uses.
+ */
+ public String getRyaVersion() {
+ return version;
+ }
+
+ /**
+ * @return Information about the instance's Entity Centric Index.
+ */
+ public EntityCentricIndexDetails getEntityCentricIndexDetails() {
+ return entityCentricDetails;
+ }
+
+ /**
+ * @return Information about the instance's Geospatial Index.
+ */
+ public GeoIndexDetails getGeoIndexDetails() {
+ return geoDetails;
+ }
+
+ /**
+ * @return Information about the instance's Precomputed Join Index.
+ */
+ public PCJIndexDetails getPCJIndexDetails() {
+ return pcjDetails;
+ }
+
+ /**
+ * @return Information about the instance's Temporal Index.
+ */
+ public TemporalIndexDetails getTemporalIndexDetails() {
+ return temporalDetails;
+ }
+
+ /**
+ * @return Information about the instance's Free Text Index.
+ */
+ public FreeTextIndexDetails getFreeTextIndexDetails() {
+ return freeTextDetails;
+ }
+
+ /**
+ * @return Information about the instance's Prospector Statistics.
+ */
+ public ProspectorDetails getProspectorDetails() {
+ return prospectorDetails;
+ }
+
+ /**
+ * @return Information about the instance's Join Selectivity Statistics.
+ */
+ public JoinSelectivityDetails getJoinSelectivityDetails() {
+ return joinSelectivityDetails;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ instanceName,
+ version,
+ entityCentricDetails,
+ geoDetails,
+ pcjDetails,
+ temporalDetails,
+ freeTextDetails,
+ prospectorDetails,
+ joinSelectivityDetails);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof RyaDetails) {
+ final RyaDetails details = (RyaDetails) obj;
+ return Objects.equals(instanceName, details.instanceName) &&
+ Objects.equals(version, details.version) &&
+ Objects.equals(entityCentricDetails, details.entityCentricDetails) &&
+ Objects.equals(geoDetails, details.geoDetails) &&
+ Objects.equals(pcjDetails, details.pcjDetails) &&
+ Objects.equals(temporalDetails, details.temporalDetails) &&
+ Objects.equals(freeTextDetails, details.freeTextDetails) &&
+ Objects.equals(prospectorDetails, details.prospectorDetails) &&
+ Objects.equals(joinSelectivityDetails, details.joinSelectivityDetails);
+ }
+ return false;
+ }
+
+ /**
+ * @return An empty instance of {@link Builder}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builds instances of {@link RyaDetails}.
+ */
+ @ParametersAreNonnullByDefault
+ public static class Builder {
+
+ // General metadata about the instance.
+ private String instanceName;
+ private String version;
+
+ // Secondary Index Details.
+ private EntityCentricIndexDetails entityCentricDetails;
+ private GeoIndexDetails geoDetails;
+ private PCJIndexDetails pcjDetails;
+ private TemporalIndexDetails temporalDetails;
+ private FreeTextIndexDetails freeTextDetails;
+
+ // Statistics Details.
+ private ProspectorDetails prospectorDetails;
+ private JoinSelectivityDetails joinSelectivityDetails;
+
+ /**
+ * Construcst an empty instance of {@link Builder}.
+ */
+ public Builder() { }
+
+ /**
+ * Constructs an instance of {@link Builder} that is initialized with
+ * a {@link RyaDetails}'s values.
+ *
+ * @param detials - The builder will be initialized with this object's values. (not null)
+ */
+ 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;
+ }
+
+ /**
+ * @param instanceName - The name that uniquely identifies the instance of Rya within
+ * the system that hosts it.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setRyaInstanceName(@Nullable final String instanceName) {
+ this.instanceName = instanceName;
+ return this;
+ }
+
+ /**
+ * @param version - The version of Rya this instance uses.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setRyaVersion(@Nullable final String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * @param entityCentricDetails - Information about the instance's Entity Centric Index.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setEntityCentricIndexDetails(@Nullable final EntityCentricIndexDetails entityCentricDetails) {
+ this.entityCentricDetails = entityCentricDetails;
+ return this;
+ }
+
+ /**
+ *
+ * @param geoDetails - Information about the instance's Geospatial Index.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setGeoIndexDetails(@Nullable final GeoIndexDetails geoDetails) {
+ this.geoDetails = geoDetails;
+ return this;
+ }
+
+ /**
+ * @param temporalDetails - Information about the instance's Temporal Index.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setTemporalIndexDetails(@Nullable final TemporalIndexDetails temporalDetails) {
+ this.temporalDetails = temporalDetails;
+ return this;
+ }
+
+ /**
+ * @param freeTextDetails - Information about the instance's Free Text Index.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setFreeTextDetails(@Nullable final FreeTextIndexDetails freeTextDetails) {
+ this.freeTextDetails = freeTextDetails;
+ return this;
+ }
+
+ /**
+ * @param pcjDetails - Information about the instance's Precomputed Join Index.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setPCJIndexDetails(@Nullable final PCJIndexDetails pcjDetails) {
+ this.pcjDetails = pcjDetails;
+ return this;
+ }
+
+ /**
+ * @param prospectorDetails - Information about the instance's Prospector Statistics.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setProspectorDetails(@Nullable final ProspectorDetails prospectorDetails) {
+ this.prospectorDetails = prospectorDetails;
+ return this;
+ }
+
+ /**
+ * @param joinSelectivityDetails - Information about the instance's Join Selectivity Statistics.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setJoinSelectivityDetails(@Nullable final JoinSelectivityDetails joinSelectivityDetails) {
+ this.joinSelectivityDetails = joinSelectivityDetails;
+ return this;
+ }
+
+ /**
+ * @return An instance of {@link RyaDetails} built using this
+ * builder's values.
+ */
+ public RyaDetails build() {
+ return new RyaDetails(
+ instanceName,
+ version,
+ entityCentricDetails,
+ geoDetails,
+ pcjDetails,
+ temporalDetails,
+ freeTextDetails,
+ prospectorDetails,
+ joinSelectivityDetails);
+ }
+ }
+
+ /**
+ * Details about a Rya instance's Geospatial Index.
+ */
+ @Immutable
+ @ParametersAreNonnullByDefault
+ public static class GeoIndexDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final boolean enabled;
+
+ /**
+ * Constructs an instance of {@link GeoIndexDetails}.
+ *
+ * @param enabled - Whether or not a Geospatial Index will be maintained by the Rya instance.
+ */
+ public GeoIndexDetails(final boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * @return Whether or not a Geospatial Index will be maintained by the Rya instance.
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash( enabled );
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof GeoIndexDetails) {
+ final GeoIndexDetails details = (GeoIndexDetails) obj;
+ return enabled == details.enabled;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Details about a Rya instance's Temporal Index.
+ */
+ @Immutable
+ @ParametersAreNonnullByDefault
+ public static class TemporalIndexDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final boolean enabled;
+
+ /**
+ * Constructs an instance of {@link TemporalIndexDetails}.
+ *
+ * @param enabled - Whether or not a Temporal Index will be maintained by the Rya instance.
+ */
+ public TemporalIndexDetails(final boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * @return Whether or not a Temporal Index will be maintained by the Rya instance.
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash( enabled );
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof TemporalIndexDetails) {
+ final TemporalIndexDetails details = (TemporalIndexDetails) obj;
+ return enabled == details.enabled;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Details about a Rya instance's Entity Centric Index.
+ */
+ @Immutable
+ @ParametersAreNonnullByDefault
+ public static class EntityCentricIndexDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final boolean enabled;
+
+ /**
+ * Constructs an instance of {@link EntityCentricIndexDetails}.
+ *
+ * @param enabled - Whether or not a Entity Centric Index will be maintained by the Rya instance.
+ */
+ public EntityCentricIndexDetails(final boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * @return Whether or not a Entity Centric Index will be maintained by the Rya instance.
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash( enabled );
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof EntityCentricIndexDetails) {
+ final EntityCentricIndexDetails details = (EntityCentricIndexDetails) obj;
+ return enabled == details.enabled;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Details about a Rya instance's Free Text Index.
+ */
+ @Immutable
+ @ParametersAreNonnullByDefault
+ public static class FreeTextIndexDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final boolean enabled;
+
+ /**
+ * Constructs an instance of {@link FreeTextIndexDetails}.
+ *
+ * @param enabled - Whether or not a Free Text Index will be maintained by the Rya instance.
+ */
+ public FreeTextIndexDetails(final boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * @return Whether or not a Free Text Index will be maintained by the Rya instance.
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash( enabled );
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof FreeTextIndexDetails) {
+ final FreeTextIndexDetails details = (FreeTextIndexDetails) obj;
+ return enabled == details.enabled;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Details about a Rya instance's PCJ Index.
+ */
+ @Immutable
+ @ParametersAreNonnullByDefault
+ public static class PCJIndexDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ public final boolean enabled;
+ private final Optional<FluoDetails> fluoDetails;
+ private final ImmutableList<PCJDetails> pcjDetails;
+
+ /**
+ * Private to prevent initialization through the constructor. To build
+ * instances of this class, use the {@link Builder}.
+ *
+ * @param enabled - Whether or not a Precomputed Join Index will be maintained by the Rya instance.
+ * @param fluoDetails - Details about a Fluo application that is used to
+ * incrementally update PCJs if one has been installed for this RYA
+ * instance. (not null)
+ * @param pcjDetails - Details about the PCJs that have been created
+ * for this Rya instance. (not null)
+ */
+ private PCJIndexDetails(
+ final boolean enabled,
+ final Optional<FluoDetails> fluoDetails,
+ final ImmutableList<PCJDetails> pcjDetails) {
+ this.enabled = enabled;
+ this.fluoDetails = requireNonNull(fluoDetails);
+ this.pcjDetails = requireNonNull(pcjDetails);
+ }
+
+ /**
+ * @return Whether or not a Precomputed Join Index will be maintained by the Rya instance.
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * @return Details about a Fluo application that is used to incrementally
+ * update PCJs if one has been installed for this RYA instance.
+ */
+ public Optional<FluoDetails> getFluoDetails() {
+ return fluoDetails;
+ }
+
+ /**
+ * @return Details about the PCJs that have been created for this Rya instance.
+ */
+ public ImmutableList<PCJDetails> getPCJDetails() {
+ return pcjDetails;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(enabled, fluoDetails, pcjDetails);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof PCJIndexDetails) {
+ final PCJIndexDetails details = (PCJIndexDetails) obj;
+ return Objects.equals(enabled, details.enabled) &&
+ Objects.equals(fluoDetails, details.fluoDetails) &&
+ Objects.equals(pcjDetails, details.pcjDetails);
+ }
+ return false;
+ }
+
+ /**
+ * @return A new instance of {@link Builder}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builds instance of {@link PCJIndexDetails).
+ */
+ @ParametersAreNonnullByDefault
+ public static class Builder {
+
+ private Boolean enabled = null;
+ private FluoDetails fluoDetails = null;
+ private final ImmutableList.Builder<PCJDetails> pcjDetails = ImmutableList.builder();
+
+ /**
+ * @param enabled - Whether or not a Precomputed Join Index will be maintained by the Rya instance.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setEnabled(final Boolean enabled) {
+ this.enabled = enabled;
+ return this;
+ }
+
+ /**
+ * @param fluoDetails - Details about a Fluo application that is used
+ * to incrementally update PCJs if one has been installed for this RYA instance.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setFluoDetails(@Nullable final FluoDetails fluoDetails) {
+ this.fluoDetails = fluoDetails;
+ return this;
+ }
+
+ /**
+ * @param pcjDetails - Details about the PCJs that have been created for this Rya instance.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder addPCJDetails(@Nullable final PCJDetails pcjDetails) {
+ this.pcjDetails.add( pcjDetails );
+ return this;
+ }
+
+ /**
+ * @return Builds an instance of {@link PCJIndexDetails} using this builder's values.
+ */
+ public PCJIndexDetails build() {
+ return new PCJIndexDetails(
+ enabled,
+ Optional.fromNullable( fluoDetails ),
+ pcjDetails.build());
+ }
+ }
+
+ /**
+ * Details about a Fluo Incremental PCJ application that has been installed
+ * as part of this Rya instance.
+ */
+ @Immutable
+ @ParametersAreNonnullByDefault
+ public static class FluoDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final String updateAppName;
+
+ /**
+ * Constructs an instance of {@link FluoDetails}.
+ *
+ * @param updateAppName - The name of the Fluo application that is
+ * updating this Rya instance's incremental PCJs. (not null)
+ */
+ public FluoDetails(final String updateAppName) {
+ this.updateAppName = requireNonNull(updateAppName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(updateAppName);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof FluoDetails) {
+ final FluoDetails details = (FluoDetails) obj;
+ return Objects.equals(updateAppName, details.updateAppName);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Details about a specific PCJ that is being maintained within the Rya instance.
+ */
+ @Immutable
+ @ParametersAreNonnullByDefault
+ public static class PCJDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final String id;
+ private final PCJUpdateStrategy updateStrategy;
+ private final Optional<Date> lastUpdateTime;
+
+ /**
+ * Private to prevent initialization through the constructor. To build
+ * instances of this class, use the {@link Builder}.
+ *
+ * @param id - Uniquely identifies the PCJ within this instance of Rya. (not null)
+ * @param updateStrategy - Describes how the PCJ is being updated. (not null)
+ * @param lastUpdateTime - The last time the PCJ was updated. This information
+ * may not be provided. (not null)
+ */
+ private PCJDetails(
+ final String id,
+ final PCJUpdateStrategy updateStrategy,
+ final Optional<Date> lastUpdateTime) {
+ this.id = requireNonNull(id);
+ this.updateStrategy = requireNonNull(updateStrategy);
+ this.lastUpdateTime = requireNonNull(lastUpdateTime);
+ }
+
+ /**
+ * @return Uniquely identifies the PCJ within this instance of Rya.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * @return Describes how the PCJ is being updated.
+ */
+ public PCJUpdateStrategy getUpdateStrategy() {
+ return updateStrategy;
+ }
+
+ /**
+ * @return The last time the PCJ was updated. This information
+ * may not be provided.
+ */
+ public Optional<Date> getLastUpdateTime() {
+ return lastUpdateTime;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, updateStrategy, lastUpdateTime);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof PCJDetails) {
+ final PCJDetails details = (PCJDetails) obj;
+ return Objects.equals(id, details.id) &&
+ Objects.equals(updateStrategy, details.updateStrategy) &&
+ Objects.equals(lastUpdateTime, details.lastUpdateTime);
+ }
+ return false;
+ }
+
+ /**
+ * @return A new instance of {@link Builder}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builds instance of {@link PCJDetails}.
+ */
+ @ParametersAreNonnullByDefault
+ public static class Builder {
+
+ private String id;
+ private PCJUpdateStrategy updateStrategy;
+ private Date lastUpdateTime;
+
+ /**
+ * @param id - Uniquely identifies the PCJ within this instance of Rya.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setId(@Nullable final String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * @param updateStrategy - Describes how the PCJ is being updated.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setUpdateStrategy(@Nullable final PCJUpdateStrategy updateStrategy) {
+ this.updateStrategy = updateStrategy;
+ return this;
+ }
+
+ /**
+ * @param lastUpdateTime - The last time the PCJ was updated. This information
+ * may not be provided.
+ * @return This {@link Builder} so that method invocations may be chained.
+ */
+ public Builder setLastUpdateTime(@Nullable final Date lastUpdateTime) {
+ this.lastUpdateTime = lastUpdateTime;
+ return this;
+ }
+
+ /**
+ * @return An instance of {@link PCJDetails} built using this builder's values.
+ */
+ public PCJDetails build() {
+ return new PCJDetails(id, updateStrategy, Optional.fromNullable(lastUpdateTime));
+ }
+ }
+
+ /**
+ * Describes the different strategies that may be used to update a PCJ index.
+ */
+ public static enum PCJUpdateStrategy {
+ /**
+ * The PCJ is being updated by periodically rebuilding all of the results.
+ */
+ BATCH,
+
+ /**
+ * The PCJ is being updated frequently and incrementally as new
+ * Statements are inserted into the Tya instance.
+ */
+ INCREMENTAL;
+ }
+ }
+ }
+
+ /**
+ * Details about a Rya instance's Prospector statistics.
+ */
+ @Immutable
+ @ParametersAreNonnullByDefault
+ public static class ProspectorDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final Optional<Date> lastUpdated;
+
+ /**
+ * Constructs an instance of {@link ProspectorDetails}.
+ *
+ * @param lastUpdated - The last time the Prospector statistics were updated for the Rya instance. (not null)
+ */
+ public ProspectorDetails(final Optional<Date> lastUpdated) {
+ this.lastUpdated = requireNonNull(lastUpdated);
+ }
+
+ /**
+ * @return The last time the Prospector statistics were updated for the Rya instance.
+ */
+ public Optional<Date> getLastUpdated() {
+ return lastUpdated;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash( lastUpdated );
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof ProspectorDetails) {
+ final ProspectorDetails details = (ProspectorDetails) obj;
+ return Objects.equals(lastUpdated, details.lastUpdated);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Details about a Rya instance's Join Selectivity statistics.
+ */
+ @Immutable
+ @ParametersAreNonnullByDefault
+ public static class JoinSelectivityDetails implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final Optional<Date> lastUpdated;
+
+ /**
+ * Constructs an instance of {@link JoinSelectivityDetails}.
+ *
+ * @param lastUpdated - The last time the Join Selectivity statistics were updated for the Rya instance. (not null)
+ */
+ public JoinSelectivityDetails(final Optional<Date> lastUpdated) {
+ this.lastUpdated = requireNonNull(lastUpdated);
+ }
+
+ /**
+ * @return The last time the Join Selectivity statistics were updated for the Rya instance.
+ */
+ public Optional<Date> getLastUpdated() {
+ return lastUpdated;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash( lastUpdated );
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(obj instanceof JoinSelectivityDetails) {
+ final JoinSelectivityDetails details = (JoinSelectivityDetails) obj;
+ return Objects.equals(lastUpdated, details.lastUpdated);
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9bdbbf5d/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetailsRepository.java
----------------------------------------------------------------------
diff --git a/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetailsRepository.java b/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetailsRepository.java
new file mode 100644
index 0000000..d05c65a
--- /dev/null
+++ b/common/rya.api/src/main/java/mvm/rya/api/instance/RyaDetailsRepository.java
@@ -0,0 +1,123 @@
+package mvm.rya.api.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 javax.annotation.ParametersAreNonnullByDefault;
+
+/**
+ * Provides access to the {@link RyaDetails} information that describes
+ * an instance of Rya.
+ */
+@ParametersAreNonnullByDefault
+public interface RyaDetailsRepository {
+
+ /**
+ * Check whether the details for this instance of Rya have been initialized or not.
+ *
+ * @return {@code true} if it has been initialized; otherwise {@code false}.
+ * @throws RyaDetailsRepositoryException Something caused this operation to fail.
+ */
+ public boolean isInitialized() throws RyaDetailsRepositoryException;
+
+ /**
+ * Initializes the {@link RyaDetails} that is stored for an instance of Rya.
+ *
+ * @param details - A Rya instance's details at installation time. (not null)
+ * @throws AlreadyInitializedException This repository has already been initialized.
+ * @throws RyaDetailsRepositoryException Something caused this operation to fail.
+ */
+ public void initialize(RyaDetails details) throws AlreadyInitializedException, RyaDetailsRepositoryException;
+
+ /**
+ * Get the {@link RyaDetails} that describe this instance of Rya.
+ *
+ * @return The details that describe this instance of Rya.
+ * @throws NotInitializedException The Rya instance's details have not been initialized yet.
+ * @throws RyaDetailsRepositoryException Something caused this operation to fail.
+ */
+ public RyaDetails getRyaInstanceDetails() throws NotInitializedException, RyaDetailsRepositoryException;
+
+ /**
+ * Update the {@link RyaDetails} that describe this instance of Rya.
+ *
+ * @param oldDetails - The copy of the details that have been updated. (not null)
+ * @param newDetails - The updated details. (not null)
+ * @throws NotInitializedException The Rya instance's details have not been initialized yet.
+ * @throws ConcurrentUpdateException An update couldn't be performed because
+ * the old state of the object no longer matches the current state.
+ * @throws RyaDetailsRepositoryException Something caused this operation to fail.
+ */
+ public void update(RyaDetails oldDetails, RyaDetails newDetails) throws NotInitializedException, ConcurrentUpdateException, RyaDetailsRepositoryException;
+
+ /**
+ * The root exception of all {@link RyaDetailsRepository} operations.
+ */
+ public static class RyaDetailsRepositoryException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public RyaDetailsRepositoryException(final String message) {
+ super(message);
+ }
+
+ public RyaDetailsRepositoryException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ /**
+ * You can not initialize a {@link RyaDetailsRepository} that has already
+ * been initialized.
+ */
+ public static class AlreadyInitializedException extends RyaDetailsRepositoryException {
+ private static final long serialVersionUID = 1L;
+
+ public AlreadyInitializedException(final String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * Some methods of {@link RyaDetailsRepository} may only be invoked after
+ * it has been initialized.
+ */
+ public static class NotInitializedException extends RyaDetailsRepositoryException {
+ private static final long serialVersionUID = 1L;
+
+ public NotInitializedException(final String message) {
+ super(message);
+ }
+
+ public NotInitializedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ /**
+ * An update couldn't be performed because the old state of the object no
+ * longer matches the current state.
+ */
+ public static class ConcurrentUpdateException extends RyaDetailsRepositoryException {
+ private static final long serialVersionUID = 1L;
+
+ public ConcurrentUpdateException(final String message) {
+ super(message);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9bdbbf5d/common/rya.api/src/test/java/mvm/rya/api/instance/RyaDetailsTest.java
----------------------------------------------------------------------
diff --git a/common/rya.api/src/test/java/mvm/rya/api/instance/RyaDetailsTest.java b/common/rya.api/src/test/java/mvm/rya/api/instance/RyaDetailsTest.java
new file mode 100644
index 0000000..c629fb6
--- /dev/null
+++ b/common/rya.api/src/test/java/mvm/rya/api/instance/RyaDetailsTest.java
@@ -0,0 +1,149 @@
+package mvm.rya.api.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 java.util.Date;
+
+import org.junit.Test;
+
+import com.google.common.base.Optional;
+
+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;
+
+/**
+ * Tests the methods of {@link RyaDetails}.
+ */
+public class RyaDetailsTest {
+
+ @Test
+ public void equals() {
+ final RyaDetails.Builder builder = RyaDetails.builder();
+
+ builder.setRyaInstanceName("test_instance")
+ .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())) );
+
+ final RyaDetails details1 = builder.build();
+ final RyaDetails details2 = builder.build();
+ assertEquals(details1, details2);
+ }
+
+ @Test
+ public void hashcode() {
+ final RyaDetails.Builder builder = RyaDetails.builder();
+
+ builder.setRyaInstanceName("test_instance")
+ .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())) );
+
+ final RyaDetails details1 = builder.build();
+ final RyaDetails details2 = builder.build();
+ assertEquals(details1.hashCode(), details2.hashCode());
+ }
+
+ @Test
+ public void constructor() {
+ final RyaDetails originalDetails = RyaDetails.builder()
+ .setRyaInstanceName("test_instance")
+ .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();
+
+ // Create a new Builder using another RyaDetails object.
+ final RyaDetails.Builder builder = new RyaDetails.Builder( originalDetails );
+
+ // Show it builds the object that was passed into it.
+ assertEquals(originalDetails, builder.build());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9bdbbf5d/dao/accumulo.rya/pom.xml
----------------------------------------------------------------------
diff --git a/dao/accumulo.rya/pom.xml b/dao/accumulo.rya/pom.xml
index 295b755..1fa6f55 100644
--- a/dao/accumulo.rya/pom.xml
+++ b/dao/accumulo.rya/pom.xml
@@ -36,15 +36,105 @@ under the License.
<artifactId>rya.api</artifactId>
</dependency>
+ <!-- Accumulo deps -->
<dependency>
<groupId>org.apache.accumulo</groupId>
<artifactId>accumulo-core</artifactId>
</dependency>
<dependency>
+ <groupId>org.openrdf.sesame</groupId>
+ <artifactId>sesame-rio-ntriples</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.openrdf.sesame</groupId>
+ <artifactId>sesame-rio-nquads</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.openrdf.sesame</groupId>
+ <artifactId>sesame-queryalgebra-evaluation</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openrdf.sesame</groupId>
+ <artifactId>sesame-rio-trig</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.mrunit</groupId>
+ <artifactId>mrunit</artifactId>
+ <classifier>hadoop2</classifier>
+ <version>1.1.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.accumulo</groupId>
+ <artifactId>accumulo-minicluster</artifactId>
+ <version>${accumulo.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <!-- RDF data Files -->
+ <exclude>**/*.ntriples</exclude>
+ <exclude>**/*.trig</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>mr</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <executions>
+ <execution>
+ <configuration>
+ <transformers>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
+ </transformers>
+ </configuration>
+ </execution>
+ </executions>
+
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
</project>
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9bdbbf5d/dao/accumulo.rya/src/main/java/mvm/rya/accumulo/instance/AccumuloRyaInstanceDetailsRepository.java
----------------------------------------------------------------------
diff --git a/dao/accumulo.rya/src/main/java/mvm/rya/accumulo/instance/AccumuloRyaInstanceDetailsRepository.java b/dao/accumulo.rya/src/main/java/mvm/rya/accumulo/instance/AccumuloRyaInstanceDetailsRepository.java
new file mode 100644
index 0000000..ec9f266
--- /dev/null
+++ b/dao/accumulo.rya/src/main/java/mvm/rya/accumulo/instance/AccumuloRyaInstanceDetailsRepository.java
@@ -0,0 +1,229 @@
+package mvm.rya.accumulo.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 java.util.Objects.requireNonNull;
+
+import java.util.Map.Entry;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.BatchWriter;
+import org.apache.accumulo.core.client.BatchWriterConfig;
+import org.apache.accumulo.core.client.ConditionalWriter;
+import org.apache.accumulo.core.client.ConditionalWriter.Result;
+import org.apache.accumulo.core.client.ConditionalWriterConfig;
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.MutationsRejectedException;
+import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.client.TableExistsException;
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.admin.TableOperations;
+import org.apache.accumulo.core.data.Condition;
+import org.apache.accumulo.core.data.ConditionalMutation;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Mutation;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.hadoop.io.Text;
+
+import mvm.rya.api.instance.RyaDetails;
+import mvm.rya.api.instance.RyaDetailsRepository;
+
+/**
+ * An implementation of {@link RyaDetailsRepository} that stores a Rya
+ * instance's {@link RyaDetails} in an Accumulo table.
+ * </p>
+ * XXX
+ * This implementation writes the details object as a serialized byte array to
+ * a row in Accumulo. Storing the entire structure within a single value is
+ * attractive because Accumulo's conditional writer will let us do checkAndSet
+ * style operations to synchronize writes to the object. On the downside, only
+ * Java clients will work.
+ */
+@ParametersAreNonnullByDefault
+public class AccumuloRyaInstanceDetailsRepository implements RyaDetailsRepository {
+
+ private static final String INSTANCE_DETAILS_TABLE_NAME = "instance_details";
+
+ private static final Text ROW_ID = new Text("instance metadata");
+ private static final Text COL_FAMILY = new Text("instance");
+ private static final Text COL_QUALIFIER = new Text("details");
+
+ private final RyaDetailsSerializer serializer = new RyaDetailsSerializer();
+
+ private final Connector connector;
+ private final String instanceName;
+ private final String detailsTableName;
+
+
+ /**
+ * Constructs an instance of {@link AccumuloRyaInstanceDetailsRepository}.
+ *
+ * @param connector - Connects to the instance of Accumulo that hosts the Rya instance. (not null)
+ * @param instanceName - The name of the Rya instance this repository represents. (not null)
+ */
+ public AccumuloRyaInstanceDetailsRepository(final Connector connector, final String instanceName) {
+ this.connector = requireNonNull( connector );
+ this.instanceName = requireNonNull( instanceName );
+ this.detailsTableName = instanceName + INSTANCE_DETAILS_TABLE_NAME;
+ }
+
+ @Override
+ public boolean isInitialized() throws RyaDetailsRepositoryException {
+ try {
+ final Scanner scanner = connector.createScanner(detailsTableName, new Authorizations());
+ scanner.fetchColumn(COL_FAMILY, COL_QUALIFIER);
+ return scanner.iterator().hasNext();
+ } catch (final TableNotFoundException e) {
+ return false;
+ }
+ }
+
+ @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 table that hosts the details if it has not been created yet.
+ final TableOperations tableOps = connector.tableOperations();
+ if(!tableOps.exists(detailsTableName)) {
+ try {
+ tableOps.create(detailsTableName);
+ } catch (AccumuloException | AccumuloSecurityException | TableExistsException e) {
+ throw new RyaDetailsRepositoryException("Could not initialize the Rya instance details for the instance named '" +
+ instanceName + "' because the the table that holds that information could not be created.");
+ }
+ }
+
+ // Write the details to the table.
+ BatchWriter writer = null;
+ try {
+ writer = connector.createBatchWriter(detailsTableName, new BatchWriterConfig());
+
+ final byte[] bytes = serializer.serialize(details);
+ final Mutation mutation = new Mutation(ROW_ID);
+ mutation.put(COL_FAMILY, COL_QUALIFIER, new Value(bytes));
+ writer.addMutation( mutation );
+
+ } catch (final TableNotFoundException | MutationsRejectedException e) {
+ throw new RyaDetailsRepositoryException("Could not initialize the Rya instance details for the instance named '" + instanceName + "'.", e);
+ } finally {
+ if(writer != null) {
+ try {
+ writer.close();
+ } catch (final MutationsRejectedException e) {
+ throw new RyaDetailsRepositoryException("", e);
+ }
+ }
+ }
+ }
+
+ @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.");
+ }
+
+ // Read it from the table.
+ try {
+ // Fetch the value from the table.
+ final Scanner scanner = connector.createScanner(detailsTableName, new Authorizations());
+ scanner.fetchColumn(COL_FAMILY, COL_QUALIFIER);
+ final Entry<Key, Value> entry = scanner.iterator().next();
+
+ // Deserialize it.
+ final byte[] bytes = entry.getValue().get();
+ return serializer.deserialize( bytes );
+
+ } catch (final TableNotFoundException e) {
+ throw new RyaDetailsRepositoryException("Could not get the details from the table.", 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.");
+ }
+
+ // Use a conditional writer so that we can detect when the old details
+ // are no longer the currently stored ones.
+ ConditionalWriter writer = null;
+ try {
+ // Setup the condition that ensures the details have not changed since the edits were made.
+ final byte[] oldDetailsBytes = serializer.serialize(oldDetails);
+ final Condition condition = new Condition(COL_FAMILY, COL_QUALIFIER);
+ condition.setValue( oldDetailsBytes );
+
+ // Create the mutation that only performs the update if the details haven't changed.
+ final ConditionalMutation mutation = new ConditionalMutation(ROW_ID);
+ mutation.addCondition( condition );
+ final byte[] newDetailsBytes = serializer.serialize(newDetails);
+ mutation.put(COL_FAMILY, COL_QUALIFIER, new Value(newDetailsBytes));
+
+ // Do the write.
+ writer = connector.createConditionalWriter(detailsTableName, new ConditionalWriterConfig());
+ final Result result = writer.write(mutation);
+ switch(result.getStatus()) {
+ case REJECTED:
+ case VIOLATED:
+ throw new ConcurrentUpdateException("Could not update the details for the Rya instance named '" +
+ instanceName + "' because the old value is out of date.");
+ case UNKNOWN:
+ case INVISIBLE_VISIBILITY:
+ throw new RyaDetailsRepositoryException("Could not update the details for the Rya instance named '" + instanceName + "'.");
+ }
+ } catch (final TableNotFoundException | AccumuloException | AccumuloSecurityException e) {
+ throw new RyaDetailsRepositoryException("Could not update the details for the Rya instance named '" + instanceName + "'.");
+ } finally {
+ if(writer != null) {
+ writer.close();
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9bdbbf5d/dao/accumulo.rya/src/main/java/mvm/rya/accumulo/instance/RyaDetailsSerializer.java
----------------------------------------------------------------------
diff --git a/dao/accumulo.rya/src/main/java/mvm/rya/accumulo/instance/RyaDetailsSerializer.java b/dao/accumulo.rya/src/main/java/mvm/rya/accumulo/instance/RyaDetailsSerializer.java
new file mode 100644
index 0000000..8c863ea
--- /dev/null
+++ b/dao/accumulo.rya/src/main/java/mvm/rya/accumulo/instance/RyaDetailsSerializer.java
@@ -0,0 +1,96 @@
+package mvm.rya.accumulo.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 java.util.Objects.requireNonNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import mvm.rya.api.instance.RyaDetails;
+import mvm.rya.api.instance.RyaDetailsRepository.RyaDetailsRepositoryException;
+
+/**
+ * Serializes {@link RyaDetails} instances.
+ */
+@ParametersAreNonnullByDefault
+public class RyaDetailsSerializer {
+
+ /**
+ * Serializes an instance of {@link RyaDetails}.
+ *
+ * @param details - The details that will be serialized. (not null)
+ * @return The serialized details.
+ */
+ public byte[] serialize(final RyaDetails details) throws SerializationException {
+ requireNonNull(details);
+
+ try {
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ new ObjectOutputStream(stream).writeObject( details );
+ return stream.toByteArray();
+ } catch (final IOException e) {
+ throw new SerializationException("Could not serialize an instance of RyaDetails.", e);
+ }
+ }
+
+ /**
+ * Deserializes an instance of {@link RyaDetails}.
+ *
+ * @param bytes - The serialized for of a {@link RyaDetails}. (not null)
+ * @return The deserialized object.
+ */
+ public RyaDetails deserialize(final byte[] bytes) throws SerializationException {
+ requireNonNull(bytes);
+
+ try {
+ final ByteArrayInputStream stream = new ByteArrayInputStream( bytes );
+ final Object o = new ObjectInputStream( stream ).readObject();
+
+ if(! (o instanceof RyaDetails) ) {
+ throw new SerializationException("Wrong type of object was deserialized. Class: " + o.getClass().getName() );
+ }
+
+ return (RyaDetails) o;
+ } catch (final ClassNotFoundException | IOException e) {
+ throw new SerializationException("Could not deserialize an instance of RyaDetails.", e);
+ }
+ }
+
+ /**
+ * Could not serialize an instance of {@link RyaDetails}.
+ */
+ public static class SerializationException extends RyaDetailsRepositoryException {
+ private static final long serialVersionUID = 1L;
+
+ public SerializationException(final String message) {
+ super(message);
+ }
+
+ public SerializationException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/9bdbbf5d/dao/accumulo.rya/src/test/java/mvm/rya/accumulo/instance/AccumuloRyaDetailsRepositoryIT.java
----------------------------------------------------------------------
diff --git a/dao/accumulo.rya/src/test/java/mvm/rya/accumulo/instance/AccumuloRyaDetailsRepositoryIT.java b/dao/accumulo.rya/src/test/java/mvm/rya/accumulo/instance/AccumuloRyaDetailsRepositoryIT.java
new file mode 100644
index 0000000..8df8a9b
--- /dev/null
+++ b/dao/accumulo.rya/src/test/java/mvm/rya/accumulo/instance/AccumuloRyaDetailsRepositoryIT.java
@@ -0,0 +1,368 @@
+package mvm.rya.accumulo.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.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.Instance;
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.ZooKeeperInstance;
+import org.apache.accumulo.core.client.admin.TableOperations;
+import org.apache.accumulo.core.client.mock.MockInstance;
+import org.apache.accumulo.core.client.security.tokens.PasswordToken;
+import org.apache.accumulo.minicluster.MiniAccumuloCluster;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.base.Optional;
+
+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 AccumuloRyaDetailsRepositoryIT {
+
+ private static MiniAccumuloCluster cluster = null;
+ private static List<String> originalTableNames = new ArrayList<>();
+
+ @BeforeClass
+ public static void startMiniAccumulo() throws IOException, InterruptedException, AccumuloException, AccumuloSecurityException {
+ // Setup the mini cluster.
+ final File tempDirectory = Files.createTempDirectory("testDir").toFile();
+ cluster = new MiniAccumuloCluster(tempDirectory, "password");
+ cluster.start();
+
+ // Store a list of the original table names.
+ final Instance instance = new ZooKeeperInstance(cluster.getInstanceName(), cluster.getZooKeepers());
+ final Connector connector = instance.getConnector("root", new PasswordToken("password"));
+ originalTableNames.addAll( connector.tableOperations().list() );
+ }
+
+ @Before
+ public void clearLastTest() throws AccumuloException, AccumuloSecurityException, TableNotFoundException {
+ // Get a list of the tables that have been added by the test.
+ final Instance instance = new ZooKeeperInstance(cluster.getInstanceName(), cluster.getZooKeepers());
+ final Connector connector = instance.getConnector("root", new PasswordToken("password"));
+ final TableOperations tableOps = connector.tableOperations();
+
+ final List<String> newTables = new ArrayList<>();
+ newTables.addAll( tableOps.list() );
+ newTables.removeAll( originalTableNames );
+
+ // Delete all the new tables.
+ for(final String newTable : newTables) {
+ tableOps.delete( newTable );
+ }
+ }
+
+ @AfterClass
+ public static void stopMiniAccumulo() throws IOException, InterruptedException {
+ cluster.stop();
+ }
+
+ @Test
+ public void initializeAndGet() throws AccumuloException, AccumuloSecurityException, 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 Accumulo.
+ final Instance instance = new ZooKeeperInstance(cluster.getInstanceName(), cluster.getZooKeepers());
+ final Connector connector = instance.getConnector("root", new PasswordToken("password"));
+ final RyaDetailsRepository repo = new AccumuloRyaInstanceDetailsRepository(connector, 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, AccumuloException, AccumuloSecurityException {
+ 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 Accumulo.
+ final Instance instance = new ZooKeeperInstance(cluster.getInstanceName(), cluster.getZooKeepers());
+ final Connector connector = instance.getConnector("root", new PasswordToken("password"));
+ final RyaDetailsRepository repo = new AccumuloRyaInstanceDetailsRepository(connector, instanceName);
+
+ // Initialize the repository
+ repo.initialize(details);
+
+ // Initialize it again.
+ repo.initialize(details);
+ }
+
+ @Test(expected = NotInitializedException.class)
+ public void getRyaInstance_notInitialized() throws AccumuloException, AccumuloSecurityException, NotInitializedException, RyaDetailsRepositoryException {
+ // Setup the repository that will be tested using a mock instance of Accumulo.
+ final Instance instance = new MockInstance();
+ final Connector connector = instance.getConnector("username", new PasswordToken("password"));
+ final RyaDetailsRepository repo = new AccumuloRyaInstanceDetailsRepository(connector, "testInstance");
+
+ // Try to fetch the details from the uninitialized repository.
+ repo.getRyaInstanceDetails();
+ }
+
+ @Test
+ public void isInitialized_true() throws AccumuloException, AccumuloSecurityException, 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 Accumulo.
+ final Instance instance = new ZooKeeperInstance(cluster.getInstanceName(), cluster.getZooKeepers());
+ final Connector connector = instance.getConnector("root", new PasswordToken("password"));
+ final RyaDetailsRepository repo = new AccumuloRyaInstanceDetailsRepository(connector, "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 AccumuloException, AccumuloSecurityException, RyaDetailsRepositoryException {
+ // Setup the repository that will be tested using a mock instance of Accumulo.
+ final Instance instance = new ZooKeeperInstance(cluster.getInstanceName(), cluster.getZooKeepers());
+ final Connector connector = instance.getConnector("root", new PasswordToken("password"));
+ final RyaDetailsRepository repo = new AccumuloRyaInstanceDetailsRepository(connector, "testInstance");
+
+ // Ensure the repository reports that is has not been initialized.
+ assertFalse( repo.isInitialized() );
+ }
+
+ @Test
+ public void update() throws AlreadyInitializedException, RyaDetailsRepositoryException, AccumuloException, AccumuloSecurityException {
+ 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 Accumulo.
+ final Instance instance = new ZooKeeperInstance(cluster.getInstanceName(), cluster.getZooKeepers());
+ final Connector connector = instance.getConnector("root", new PasswordToken("password"));
+ final RyaDetailsRepository repo = new AccumuloRyaInstanceDetailsRepository(connector, "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 AccumuloException, AccumuloSecurityException, 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 Accumulo.
+ final Instance instance = new ZooKeeperInstance(cluster.getInstanceName(), cluster.getZooKeepers());
+ final Connector connector = instance.getConnector("root", new PasswordToken("password"));
+ final RyaDetailsRepository repo = new AccumuloRyaInstanceDetailsRepository(connector, "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