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/master
Commit: 9bdbbf5dd9ba5f7662e0044c09a1acf17095b616
Parents: d49aa6d
Author: Kevin Chilton <[email protected]>
Authored: Sat Jun 4 17:46:43 2016 -0400
Committer: Aaron Mihalik <[email protected]>
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

Reply via email to