summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/amazon')
-rw-r--r--src/main/java/com/amazon/carbonado/Alias.java56
-rw-r--r--src/main/java/com/amazon/carbonado/AlternateKeys.java56
-rw-r--r--src/main/java/com/amazon/carbonado/ConfigurationException.java46
-rw-r--r--src/main/java/com/amazon/carbonado/ConstraintException.java47
-rw-r--r--src/main/java/com/amazon/carbonado/CorruptEncodingException.java54
-rw-r--r--src/main/java/com/amazon/carbonado/Cursor.java157
-rw-r--r--src/main/java/com/amazon/carbonado/FetchDeadlockException.java51
-rw-r--r--src/main/java/com/amazon/carbonado/FetchException.java46
-rw-r--r--src/main/java/com/amazon/carbonado/FetchInterruptedException.java45
-rw-r--r--src/main/java/com/amazon/carbonado/FetchMultipleException.java51
-rw-r--r--src/main/java/com/amazon/carbonado/FetchNoneException.java51
-rw-r--r--src/main/java/com/amazon/carbonado/FetchTimeoutException.java49
-rw-r--r--src/main/java/com/amazon/carbonado/Independent.java65
-rw-r--r--src/main/java/com/amazon/carbonado/Index.java40
-rw-r--r--src/main/java/com/amazon/carbonado/Indexes.java58
-rw-r--r--src/main/java/com/amazon/carbonado/IsolationLevel.java115
-rw-r--r--src/main/java/com/amazon/carbonado/Join.java120
-rw-r--r--src/main/java/com/amazon/carbonado/Key.java43
-rw-r--r--src/main/java/com/amazon/carbonado/MalformedArgumentException.java69
-rw-r--r--src/main/java/com/amazon/carbonado/MalformedFilterException.java72
-rw-r--r--src/main/java/com/amazon/carbonado/MalformedTypeException.java53
-rw-r--r--src/main/java/com/amazon/carbonado/MismatchException.java70
-rw-r--r--src/main/java/com/amazon/carbonado/Nullable.java44
-rw-r--r--src/main/java/com/amazon/carbonado/OptimisticLockException.java85
-rw-r--r--src/main/java/com/amazon/carbonado/PersistDeadlockException.java51
-rw-r--r--src/main/java/com/amazon/carbonado/PersistDeniedException.java46
-rw-r--r--src/main/java/com/amazon/carbonado/PersistException.java46
-rw-r--r--src/main/java/com/amazon/carbonado/PersistMultipleException.java51
-rw-r--r--src/main/java/com/amazon/carbonado/PersistNoneException.java51
-rw-r--r--src/main/java/com/amazon/carbonado/PersistTimeoutException.java49
-rw-r--r--src/main/java/com/amazon/carbonado/PrimaryKey.java50
-rw-r--r--src/main/java/com/amazon/carbonado/Query.java421
-rw-r--r--src/main/java/com/amazon/carbonado/Repository.java157
-rw-r--r--src/main/java/com/amazon/carbonado/RepositoryBuilder.java104
-rw-r--r--src/main/java/com/amazon/carbonado/RepositoryException.java220
-rw-r--r--src/main/java/com/amazon/carbonado/Sequence.java49
-rw-r--r--src/main/java/com/amazon/carbonado/Storable.java424
-rw-r--r--src/main/java/com/amazon/carbonado/Storage.java133
-rw-r--r--src/main/java/com/amazon/carbonado/SupportException.java48
-rw-r--r--src/main/java/com/amazon/carbonado/Transaction.java120
-rw-r--r--src/main/java/com/amazon/carbonado/Trigger.java327
-rw-r--r--src/main/java/com/amazon/carbonado/UniqueConstraintException.java47
-rw-r--r--src/main/java/com/amazon/carbonado/UnsupportedTypeException.java40
-rw-r--r--src/main/java/com/amazon/carbonado/Version.java78
-rw-r--r--src/main/java/com/amazon/carbonado/package-info.java22
45 files changed, 4077 insertions, 0 deletions
diff --git a/src/main/java/com/amazon/carbonado/Alias.java b/src/main/java/com/amazon/carbonado/Alias.java
new file mode 100644
index 0000000..b2e122c
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Alias.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies alternate names for a {@link Storable} or a Storable property. An alias is used
+ * only by a repository to link to entities. Without an alias, the repository will perform
+ * a best guess at finding an entity to use. Aliases may be ignored by repositories that
+ * don't require explicitly named entities.
+ * <P>The most common use for an alias is for a JDBC repository, to link a storable to a table and
+ * its properties to the corresponding columns. Naming conventions for databases rarely work
+ * well for class and variable names.
+ *
+ * <p>Example:<pre>
+ * <b>&#64;Alias("USER_INFO")</b>
+ * &#64;PrimaryKey("userInfoID")
+ * public interface UserInfo extends Storable&lt;UserInfo&gt; {
+ * <b>&#64;Alias("USER_ID")</b>
+ * long getUserInfoID();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @author Brian S O'Neill
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Alias {
+
+ /**
+ * Alias values for the storage layer to select from. It will choose the
+ * first one in the list that matches one of its own entities.
+ */
+ String[] value();
+
+}
diff --git a/src/main/java/com/amazon/carbonado/AlternateKeys.java b/src/main/java/com/amazon/carbonado/AlternateKeys.java
new file mode 100644
index 0000000..9d27f12
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/AlternateKeys.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * List of alternate keys for a {@link Storable}.
+ *
+ * <p>Example:<pre>
+ * <b>&#64;AlternateKeys</b>({
+ * <b>&#64;Key</b>("fullPath")
+ * <b>&#64;Key</b>({"+name", "-parentID"})
+ * })
+ * &#64;PrimaryKey("ID")
+ * public interface FileInfo extends Storable&lt;FileInfo&gt; {
+ * long getID();
+ *
+ * String getFullPath();
+ *
+ * String getName();
+ *
+ * long getParentID();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @author Brian S O'Neill
+ * @see Key
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface AlternateKeys {
+ /**
+ * A list of Key annotations.
+ */
+ Key[] value() default {};
+}
diff --git a/src/main/java/com/amazon/carbonado/ConfigurationException.java b/src/main/java/com/amazon/carbonado/ConfigurationException.java
new file mode 100644
index 0000000..c45ac60
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/ConfigurationException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package com.amazon.carbonado;
+
+/**
+ * Indicates that a {@link com.amazon.carbonado.Repository} could not be
+ * instantiated given the configuration information provided to a {@link
+ * com.amazon.carbonado.RepositoryBuilder}.
+ *
+ * @author Don Schneider
+ */
+public class ConfigurationException extends SupportException {
+
+ private static final long serialVersionUID = 5202481033055452633L;
+
+ public ConfigurationException() {
+ super();
+ }
+
+ public ConfigurationException(Throwable cause) {
+ super(cause);
+ }
+
+ public ConfigurationException(String message) {
+ super(message);
+ }
+
+ public ConfigurationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/ConstraintException.java b/src/main/java/com/amazon/carbonado/ConstraintException.java
new file mode 100644
index 0000000..27ae306
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/ConstraintException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A ConstraintException is thrown if the {@link Repository} storage layer
+ * detects any kind of constraint violation.
+ *
+ * @author Brian S O'Neill
+ */
+public class ConstraintException extends PersistException {
+
+ private static final long serialVersionUID = -6195914735758203580L;
+
+ public ConstraintException() {
+ super();
+ }
+
+ public ConstraintException(String message) {
+ super(message);
+ }
+
+ public ConstraintException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConstraintException(Throwable cause) {
+ super(cause);
+ }
+}
+
diff --git a/src/main/java/com/amazon/carbonado/CorruptEncodingException.java b/src/main/java/com/amazon/carbonado/CorruptEncodingException.java
new file mode 100644
index 0000000..b4dcf77
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/CorruptEncodingException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A CorruptEncodingException is caused when decoding an encoded record fails.
+ *
+ * @author Brian S O'Neill
+ */
+public class CorruptEncodingException extends FetchException {
+
+ private static final long serialVersionUID = 4543503149683482362L;
+
+ public CorruptEncodingException() {
+ super();
+ }
+
+ public CorruptEncodingException(String message) {
+ super(message);
+ }
+
+ public CorruptEncodingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CorruptEncodingException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * @param expectedGeneration expected layout generation of decoded storable
+ * @param actualGeneration actual layout generation of decoded storable
+ */
+ public CorruptEncodingException(int expectedGeneration, int actualGeneration) {
+ super("Expected layout generation of " + expectedGeneration +
+ ", but actual layout generation was " + actualGeneration);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/Cursor.java b/src/main/java/com/amazon/carbonado/Cursor.java
new file mode 100644
index 0000000..00b2dbc
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Cursor.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Represents the results of a {@link com.amazon.carbonado.Query}'s fetch
+ * operation. Cursors must be closed promptly when no longer
+ * needed. Failure to do so may result in excessive resource consumption or
+ * deadlock. As a convenience, the close operation is automatically performed
+ * when the end is reached or when an exception is thrown.
+ *
+ * <P>Note: because a Cursor manages resources, it is inapproprate to create a long-lived one and
+ * pass it around in your code. A cursor is expected to live close to the Query which vended
+ * it. To discourage inappropriate retention, the cursor does not implement methods (like
+ * "getQuery" or "reset") which would make it more convenient to operate on in isolation.
+ *
+ * <P>Similarly, it is difficult to guarantee that the results of a cursor will
+ * be the same in case of a "reset" or reverse iteration. For this reason,
+ * neither is supported; if you need to iterate the same set of objects twice,
+ * simply retain the query object and reissue it. Be aware that the results may
+ * not be identical, if any relevant objects are added to or removed the
+ * repository in the interim. To guard against this, operate within a
+ * serializable {@link IsolationLevel isolation level}.
+ *
+ * <p>Cursor instances are mutable, but they should be thread-safe. Still, only
+ * one thread should ever operate on a cursor instance.
+ *
+ * @author Brian S O'Neill
+ * @author Don Schneider
+ */
+public interface Cursor<S> {
+ /**
+ * Call close to release any resources being held by this cursor. Further
+ * operations on this cursor will behave as if there are no results.
+ */
+ void close() throws FetchException;
+
+ /**
+ * Returns true if this cursor has more elements. In other words, returns
+ * true if {@link #next next} would return an element rather than throwing
+ * an exception.
+ *
+ * @throws FetchException if storage layer throws an exception
+ */
+ boolean hasNext() throws FetchException;
+
+ /**
+ * Returns the next element from this cursor. This method may be called
+ * repeatedly to iterate through the results.
+ *
+ * @throws FetchException if storage layer throws an exception
+ * @throws NoSuchElementException if the cursor has no next element.
+ */
+ S next() throws FetchException;
+
+ /**
+ * Skips forward by the specified amount of elements, returning the actual
+ * amount skipped. The actual amount may be less than the requested amount
+ * if the end of the results was reached.
+ *
+ * @param amount maximum amount of elements to skip
+ * @return actual amount skipped
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalArgumentException if amount is negative
+ */
+ int skipNext(int amount) throws FetchException;
+
+ /**
+ * Copies all remaining next elements into the given collection. This
+ * method is roughly equivalent to the following:
+ * <pre>
+ * Cursor cursor;
+ * ...
+ * while (cursor.hasNext()) {
+ * c.add(cursor.next());
+ * }
+ * </pre>
+ *
+ * <p>As a side-effect of calling this method, the cursor is closed.
+ *
+ * @return actual amount of results added
+ * @throws FetchException if storage layer throws an exception
+ */
+ int copyInto(Collection<? super S> c) throws FetchException;
+
+ /**
+ * Copies a limited amount of remaining next elements into the given
+ * collection. This method is roughly equivalent to the following:
+ * <pre>
+ * Cursor cursor;
+ * ...
+ * while (--limit >= 0 && cursor.hasNext()) {
+ * c.add(cursor.next());
+ * }
+ * </pre>
+ *
+ * @param limit maximum amount of elements to copy
+ * @return actual amount of results added
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalArgumentException if limit is negative
+ */
+ int copyInto(Collection<? super S> c, int limit) throws FetchException;
+
+ /**
+ * Copies all remaining next elements into a new modifiable list. This
+ * method is roughly equivalent to the following:
+ * <pre>
+ * Cursor&lt;S&gt; cursor;
+ * ...
+ * List&lt;S&gt; list = new ...
+ * cursor.copyInto(list);
+ * </pre>
+ *
+ * <p>As a side-effect of calling this method, the cursor is closed.
+ *
+ * @return a new modifiable list
+ * @throws FetchException if storage layer throws an exception
+ */
+ List<S> toList() throws FetchException;
+
+ /**
+ * Copies a limited amount of remaining next elements into a new modifiable
+ * list. This method is roughly equivalent to the following:
+ * <pre>
+ * Cursor&lt;S&gt; cursor;
+ * ...
+ * List&lt;S&gt; list = new ...
+ * cursor.copyInto(list, limit);
+ * </pre>
+ *
+ * @param limit maximum amount of elements to copy
+ * @return a new modifiable list
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalArgumentException if limit is negative
+ */
+ List<S> toList(int limit) throws FetchException;
+}
diff --git a/src/main/java/com/amazon/carbonado/FetchDeadlockException.java b/src/main/java/com/amazon/carbonado/FetchDeadlockException.java
new file mode 100644
index 0000000..485231d
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/FetchDeadlockException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Thrown if a fetch operation fails because it was selected to resolve a
+ * deadlock.
+ *
+ * @author Brian S O'Neill
+ */
+public class FetchDeadlockException extends FetchException {
+
+ private static final long serialVersionUID = -1305211848299678161L;
+
+ public FetchDeadlockException() {
+ super();
+ }
+
+ public FetchDeadlockException(String message) {
+ super(message);
+ }
+
+ public FetchDeadlockException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FetchDeadlockException(Throwable cause) {
+ super(cause);
+ }
+
+ @Override
+ protected PersistException makePersistException(String message, Throwable cause) {
+ return new PersistDeadlockException(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/FetchException.java b/src/main/java/com/amazon/carbonado/FetchException.java
new file mode 100644
index 0000000..a8af267
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/FetchException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A FetchException is caused by a failure when selecting records from a {@link
+ * Repository}.
+ *
+ * @author Brian S O'Neill
+ */
+public class FetchException extends RepositoryException {
+
+ private static final long serialVersionUID = 3261554550881322593L;
+
+ public FetchException() {
+ super();
+ }
+
+ public FetchException(String message) {
+ super(message);
+ }
+
+ public FetchException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FetchException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/FetchInterruptedException.java b/src/main/java/com/amazon/carbonado/FetchInterruptedException.java
new file mode 100644
index 0000000..1f8e4a7
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/FetchInterruptedException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Thrown from a fetch operation that was canceled.
+ *
+ * @author Brian S O'Neill
+ */
+public class FetchInterruptedException extends FetchException {
+
+ private static final long serialVersionUID = -1708236034888616065L;
+
+ public FetchInterruptedException() {
+ super();
+ }
+
+ public FetchInterruptedException(String message) {
+ super(message);
+ }
+
+ public FetchInterruptedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FetchInterruptedException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/FetchMultipleException.java b/src/main/java/com/amazon/carbonado/FetchMultipleException.java
new file mode 100644
index 0000000..0fbc34e
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/FetchMultipleException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A FetchMultipleException is thrown when a fetch operation returned more
+ * than one record when at most one was expected.
+ *
+ * @author Brian S O'Neill
+ */
+public class FetchMultipleException extends FetchException {
+
+ private static final long serialVersionUID = -7126472358604146147L;
+
+ public FetchMultipleException() {
+ super();
+ }
+
+ public FetchMultipleException(String message) {
+ super(message);
+ }
+
+ public FetchMultipleException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FetchMultipleException(Throwable cause) {
+ super(cause);
+ }
+
+ @Override
+ protected PersistException makePersistException(String message, Throwable cause) {
+ return new PersistMultipleException(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/FetchNoneException.java b/src/main/java/com/amazon/carbonado/FetchNoneException.java
new file mode 100644
index 0000000..dfc4dfe
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/FetchNoneException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A FetchNoneException is thrown when a fetch operation returned no records
+ * when at least one was expected.
+ *
+ * @author Brian S O'Neill
+ */
+public class FetchNoneException extends FetchException {
+
+ private static final long serialVersionUID = -3271539536650048094L;
+
+ public FetchNoneException() {
+ super();
+ }
+
+ public FetchNoneException(String message) {
+ super(message);
+ }
+
+ public FetchNoneException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FetchNoneException(Throwable cause) {
+ super(cause);
+ }
+
+ @Override
+ protected PersistException makePersistException(String message, Throwable cause) {
+ return new PersistNoneException(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/FetchTimeoutException.java b/src/main/java/com/amazon/carbonado/FetchTimeoutException.java
new file mode 100644
index 0000000..bb5eb10
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/FetchTimeoutException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Thrown if a fetch operation fails because lock acquisition timed out.
+ *
+ * @author Brian S O'Neill
+ */
+public class FetchTimeoutException extends FetchException {
+ private static final long serialVersionUID = 1L;
+
+ public FetchTimeoutException() {
+ super();
+ }
+
+ public FetchTimeoutException(String message) {
+ super(message);
+ }
+
+ public FetchTimeoutException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FetchTimeoutException(Throwable cause) {
+ super(cause);
+ }
+
+ @Override
+ protected PersistException makePersistException(String message, Throwable cause) {
+ return new PersistTimeoutException(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/Independent.java b/src/main/java/com/amazon/carbonado/Independent.java
new file mode 100644
index 0000000..f240fe5
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Independent.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * A hint for a dependent {@link Repository} to allow a {@link Storable} property or type
+ * to be undefined in the underlying schema. Ordinarily, if a dependent repository cannot
+ * find a matching property, it throws {@link MismatchException} when the {@link Storage} is
+ * first retrieved for the storable. This annotation suppresses that exception, and instead
+ * makes the property or type unsupported. Any subsequent invocation of a property access
+ * method for the independent type or property will cause an UnsupportedOperationException
+ * to be thrown.
+ * <p>
+ * One example of when this might be used would be to store a calculated field in the cached
+ * representation of the object. It is <b>not</b> necessary to prevent implemented methods
+ * of the form "get&lt;value&gt;" from being inadvertently interpreted as properties of the
+ * storable; any implementation is by definition not a property.
+ * <p>
+ * If a correctly matching property actually is found, then this annotation is
+ * ignored and the property or type is defined as usual. If the Repository
+ * finds a property whose name matches, but whose type does not match, a
+ * MismatchException will be thrown regardless of this annotation.
+ *
+ * <P>Independent repositories completely ignore this annotation.
+ *
+ * <p>Example:<pre>
+ * public interface UserInfo extends Storable&lt;UserInfo&gt; {
+ * <b>&#64;Independent</b>
+ * String getName();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * <b>Note:</b> If a {@link Version versioned} Storable with an independent
+ * property is managed by a replicating repository, updates which modify just
+ * the independent property still update the master Storable, in order to get a
+ * new record version. Therefore, independent properties should not be used as
+ * a performance enhancement which avoids writes to a master repository.
+ *
+ * @author Brian S O'Neill
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Independent {
+}
diff --git a/src/main/java/com/amazon/carbonado/Index.java b/src/main/java/com/amazon/carbonado/Index.java
new file mode 100644
index 0000000..755ecfe
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Index.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * An Index annotation provides a hint to the storage layer allowing {@link
+ * Query queries} to be performed more quickly. Indexes are contained by an
+ * {@link Indexes} annotation.
+ *
+ * @author Brian S O'Neill
+ * @see Indexes
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({})
+public @interface Index {
+ /**
+ * A list of property names, which may be prefixed with '+' or '-' to
+ * indicate a preference for ascending or descending order.
+ */
+ String[] value();
+}
diff --git a/src/main/java/com/amazon/carbonado/Indexes.java b/src/main/java/com/amazon/carbonado/Indexes.java
new file mode 100644
index 0000000..482f955
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Indexes.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * The Indexes annotation is applied to a {@link Storable} for specifying all
+ * the {@link Index indexes} it should have.
+ *
+ * <p>Example:<pre>
+ * <b>&#64;Indexes</b>({
+ * <b>&#64;Index</b>("name"),
+ * <b>&#64;Index</b>("-lastModified"),
+ * <b>&#64;Index</b>({"length", "lastModified"})
+ * })
+ * &#64;PrimaryKey("ID")
+ * public interface FileInfo extends Storable&lt;FileInfo&gt; {
+ * long getID();
+ *
+ * String getName();
+ *
+ * long getLength();
+ *
+ * long getLastModified();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @author Brian S O'Neill
+ * @see Index
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Indexes {
+ /**
+ * A list of Index annotations.
+ */
+ Index[] value() default {};
+}
diff --git a/src/main/java/com/amazon/carbonado/IsolationLevel.java b/src/main/java/com/amazon/carbonado/IsolationLevel.java
new file mode 100644
index 0000000..5f963b2
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/IsolationLevel.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Describes a transaction isolation level. Transaction levels, in order from
+ * lowest to highest are:
+ *
+ * <ul>
+ * <li>{@link #READ_UNCOMMITTED}
+ * <li>{@link #READ_COMMITTED}
+ * <li>{@link #REPEATABLE_READ}
+ * <li>{@link #SERIALIZABLE}
+ * </ul>
+ *
+ * A transaction's isolation level is usually {@code READ_COMMITTED} or
+ * {@code REPEATABLE_READ} by default. Forcing a lower level, like
+ * {@code READ_COMMITTED}, is useful when performing a long cursor
+ * iteration. It releases locks during iteration rather than holding on to them
+ * until the transaction exits.
+ *
+ * @author Brian S O'Neill
+ * @see Repository#enterTransaction(IsolationLevel)
+ * @see Transaction
+ */
+public enum IsolationLevel {
+
+ /**
+ * Indicates that dirty reads, non-repeatable reads and phantom reads can
+ * occur. This level allows modifications by one transaction to be read by
+ * another transaction before any changes have been committed (a "dirty
+ * read"). If any of the changes are rolled back, the second transaction
+ * will have retrieved an invalid modification.
+ *
+ * <p>This level is also known as degree 1 isolation.
+ */
+ READ_UNCOMMITTED,
+
+ /**
+ * Indicates that dirty reads are prevented. Non-repeatable reads and
+ * phantom reads can occur. This level only prohibits a transaction from
+ * reading modifications with uncommitted changes in it.
+ *
+ * <p>This level is also known as degree 2 isolation.
+ */
+ READ_COMMITTED,
+
+ /**
+ * Indicates that dirty reads and non-repeatable reads are prevented.
+ * Phantom reads can occur. This level prohibits a transaction from reading
+ * uncommitted changes, and it also prohibits the situation where one
+ * transaction reads a record, a second transaction alters the record, and
+ * the first transaction rereads the record, getting different values the
+ * second time (a "non-repeatable read").
+ */
+ REPEATABLE_READ,
+
+ /**
+ * Indicates that dirty reads, non-repeatable reads and phantom reads are
+ * prevented. Phantoms are records returned as a result of a search, but
+ * which were not seen by the same transaction when the identical search
+ * criteria was previously used. For example, another transaction may have
+ * inserted records which match the original search.
+ *
+ * <p>This level is also known as degree 3 isolation.
+ */
+ SERIALIZABLE;
+
+ /**
+ * Returns true if this isolation level is at least as high as the one
+ * given.
+ */
+ public boolean isAtLeast(IsolationLevel level) {
+ return ordinal() >= level.ordinal();
+ }
+
+ /**
+ * Returns true if this isolation level is no higher than the one given.
+ */
+ public boolean isAtMost(IsolationLevel level) {
+ return ordinal() <= level.ordinal();
+ }
+
+ /**
+ * Returns the lowest common isolation level between this and the one
+ * given.
+ */
+ public IsolationLevel lowestCommon(IsolationLevel level) {
+ return (ordinal() >= level.ordinal()) ? level : this;
+ }
+
+ /**
+ * Returns the highest common isolation level between this and the one
+ * given.
+ */
+ public IsolationLevel highestCommon(IsolationLevel level) {
+ return (ordinal() <= level.ordinal()) ? level : this;
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/Join.java b/src/main/java/com/amazon/carbonado/Join.java
new file mode 100644
index 0000000..eb21e58
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Join.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies a {@link Storable} property as defining a join relationship
+ * with another Storable type. Joins can also refer to their own enclosing
+ * class or interface.
+ * <p>
+ * To complete the join, lists of internal and external properties may be
+ * supplied. If these lists are not supplied, then join is "natural", and the
+ * properties are determined automatically. When the lists are specified, the
+ * join is "explicit". Natural joins are merely a convenience; they can always
+ * be replaced by an explicit join.
+ * <p>
+ * The lists used for explicit joins must have the same length, and each must
+ * have at least one element. Each element in the internal list must refer to
+ * a property defined in this property's class or interface, and each element
+ * in the external list must refer to a matching property defined in the joined
+ * type. The matched up property pairs must not themselves be join properties,
+ * and they must be compatible with each other.
+ * <p>
+ * If the join is made to external properties which do not completely specify a
+ * primary key, then the type of the join property must be a {@link Query} of
+ * the joined type. When the type is a Query, a property mutator method cannot
+ * be defined. The returned query has all of the "with" parameters filled in.
+ * <p>
+ * With a natural join, the internal and external properties are deduced by
+ * examining the type of the referenced join property. If the type is a Query,
+ * then the internal and external properties are set to match this property's
+ * primary key. The referenced join property (specified as a parameterized type
+ * to Query) must have properties matching name and type of this property's
+ * primary key.
+ * <p>
+ * If a natural join's property type is not defined by a Query, then the
+ * internal and external properties are set to match the referenced property's
+ * primary key. This join property must have properties matching name and type
+ * of the referenced property's primary key.
+ *
+ * <p>Example:<pre>
+ * &#64;PrimaryKey("addressID")
+ * public interface Address extends Storable {
+ * int getAddressID();
+ *
+ * ...
+ * }
+ *
+ * &#64;PrimaryKey("userID")
+ * public interface UserInfo extends Storable {
+ * int getUserID();
+ *
+ * int getAddressID();
+ *
+ * void setAddressID(int value);
+ *
+ * // Natural join, which works because Address has a primary key
+ * // property of addressID which matches a property in this type.
+ * <b>&#64;Join</b>
+ * Address getAddress() throws FetchException;
+ *
+ * // Explicit join, equivalent to getAddress.
+ * <b>&#64;Join(internal="addressID", external="addressID")</b>
+ * Address getCurrentAddress() throws FetchException;
+ *
+ * &#64;Nullable
+ * Integer getParentID();
+ *
+ * void setParentID(Integer value);
+ *
+ * // Many-to-one relationship
+ * &#64;Nullable
+ * <b>&#64;Join(internal="parentID", external="userID")</b>
+ * UserInfo getParent() throws FetchException;
+ *
+ * // One-to-many relationship
+ * <b>&#64;Join(internal="userID", external="parentID")</b>
+ * Query&lt;UserInfo&gt; getChildren() throws FetchException;
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @author Brian S O'Neill
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Join {
+
+ /**
+ * List of property names defined in this property's enclosing class or
+ * interface.
+ */
+ String[] internal() default {};
+
+ /**
+ * List of property names defined in the foreign property's enclosing class
+ * or interface.
+ */
+ String[] external() default {};
+
+}
diff --git a/src/main/java/com/amazon/carbonado/Key.java b/src/main/java/com/amazon/carbonado/Key.java
new file mode 100644
index 0000000..01db2b8
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Key.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies a {@link Storable} property as being a member of an alternate
+ * key. An alternate key is just as good as the primary key for uniquely
+ * identifying a storable instance, except repositories are usually more
+ * flexible with alternate keys. For example, dropping an alternate key and
+ * reconstructing it should not result in loss of data. Alternate keys are
+ * often implemented as indexes with a uniqueness constraint.
+ *
+ * @author Brian S O'Neill
+ * @see AlternateKeys
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({})
+public @interface Key {
+ /**
+ * A list of property names, which may be prefixed with '+' or '-' to
+ * indicate a preference for ascending or descending order.
+ */
+ String[] value();
+}
diff --git a/src/main/java/com/amazon/carbonado/MalformedArgumentException.java b/src/main/java/com/amazon/carbonado/MalformedArgumentException.java
new file mode 100644
index 0000000..265974c
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/MalformedArgumentException.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A MalformedArgumentException is thrown after detailed analysis on an
+ * argument determined it was not suitable. This class is abstract to prevent
+ * its direct use. Subclasses are encouraged to provide more detail as to the
+ * cause of the exception.
+ *
+ * @author Brian S O'Neill
+ */
+public abstract class MalformedArgumentException extends IllegalArgumentException {
+
+ private static final long serialVersionUID = 1L;
+
+ private List<String> mMessages;
+
+ protected MalformedArgumentException() {
+ super();
+ }
+
+ protected MalformedArgumentException(String message) {
+ super(message);
+ }
+
+ protected MalformedArgumentException(List<String> messages) {
+ super();
+ mMessages = Collections.unmodifiableList(messages);
+ }
+
+ public String getMessage() {
+ if (mMessages == null || mMessages.size() == 0) {
+ return super.getMessage();
+ }
+ return mMessages.get(0);
+ }
+
+ /**
+ * Multiple error messages may be embedded in a MalformedArgumentException.
+ *
+ * @return non-null, unmodifiable list of messages
+ */
+ public List<String> getMessages() {
+ if (mMessages == null || mMessages.size() == 0) {
+ mMessages = Collections.singletonList(super.getMessage());
+ }
+ return mMessages;
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/MalformedFilterException.java b/src/main/java/com/amazon/carbonado/MalformedFilterException.java
new file mode 100644
index 0000000..0333c10
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/MalformedFilterException.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A MalformedFilterException indicates that a
+ * {@link Storage#query(String) query filter} is invalid.
+ *
+ * @author Brian S O'Neill
+ */
+public class MalformedFilterException extends MalformedArgumentException {
+
+ private static final long serialVersionUID = -6917096146885017974L;
+
+ private final String mFilter;
+ private final int mIndex;
+
+ public MalformedFilterException(String filter) {
+ super(filter);
+ mFilter = filter;
+ mIndex = -1;
+ }
+
+ public MalformedFilterException(String filter, int index) {
+ super(filter);
+ mFilter = filter;
+ mIndex = index;
+ }
+
+ public MalformedFilterException(String filter, String message) {
+ super(message);
+ mFilter = filter;
+ mIndex = -1;
+ }
+
+ public MalformedFilterException(String filter, String message, int index) {
+ super(message);
+ mFilter = filter;
+ mIndex = index;
+ }
+
+ /**
+ * Returns the malformed query filter.
+ */
+ public String getFilter() {
+ return mFilter;
+ }
+
+ /**
+ * Returns the index into the query filter string that is malformed, or a
+ * negative value if not known.
+ */
+ public int getCulpritIndex() {
+ return mIndex;
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/MalformedTypeException.java b/src/main/java/com/amazon/carbonado/MalformedTypeException.java
new file mode 100644
index 0000000..45ad528
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/MalformedTypeException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.util.List;
+
+/**
+ * A MalformedTypeException indicates that a {@link Storable} is defined in a
+ * way that violates the requirements for Storable objects.
+ *
+ * @author Brian S O'Neill
+ */
+public class MalformedTypeException extends MalformedArgumentException {
+
+ private static final long serialVersionUID = 5463649671507513977L;
+
+ private final Class<?> mType;
+
+ public MalformedTypeException(Class<?> malformedType) {
+ super();
+ mType = malformedType;
+ }
+
+ public MalformedTypeException(Class<?> malformedType, String message) {
+ super(message);
+ mType = malformedType;
+ }
+
+ public MalformedTypeException(Class<?> malformedType, List<String> messages) {
+ super(messages);
+ mType = malformedType;
+ }
+
+ public Class<?> getMalformedType() {
+ return mType;
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/MismatchException.java b/src/main/java/com/amazon/carbonado/MismatchException.java
new file mode 100644
index 0000000..ddd64f6
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/MismatchException.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Indicates that a {@link Storable} doesn't exactly match up with an external
+ * schema. This exception may only be thrown by repositories with a dependency
+ * on an external schema.
+ *
+ * @author Brian S O'Neill
+ */
+public class MismatchException extends SupportException {
+
+ private static final long serialVersionUID = 5840495857407789424L;
+
+ private List<String> mMessages;
+
+ public MismatchException() {
+ super();
+ mMessages = null;
+ }
+
+ public MismatchException(String message) {
+ super(message);
+ mMessages = null;
+ }
+
+ public MismatchException(List<String> messages) {
+ super();
+ mMessages = Collections.unmodifiableList(messages);
+ }
+
+ public String getMessage() {
+ if (mMessages == null || mMessages.size() == 0) {
+ return super.getMessage();
+ }
+ return mMessages.get(0);
+ }
+
+ /**
+ * Multiple error messages may be embedded in a MismatchException.
+ *
+ * @return non-null, unmodifiable list of messages
+ */
+ public List<String> getMessages() {
+ if (mMessages == null || mMessages.size() == 0) {
+ mMessages = Collections.singletonList(super.getMessage());
+ }
+ return mMessages;
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/Nullable.java b/src/main/java/com/amazon/carbonado/Nullable.java
new file mode 100644
index 0000000..c8ac923
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Nullable.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies that a {@link Storable} property can have a null value. By
+ * default, all Storable properties are required to have a non-null value. It
+ * is illegal to declare a property as nullable whose type is a primitive
+ * non-object.
+ *
+ * <p>Example:<pre>
+ * public interface UserInfo extends Storable&lt;UserInfo&gt; {
+ * <b>&#64;Nullable</b>
+ * String getName();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @author Brian S O'Neill
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Nullable {
+}
diff --git a/src/main/java/com/amazon/carbonado/OptimisticLockException.java b/src/main/java/com/amazon/carbonado/OptimisticLockException.java
new file mode 100644
index 0000000..2a782d1
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/OptimisticLockException.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * An OptimisticLockException is thrown if the {@link Repository} is using
+ * optimistic locking for concurrency control, and lock aquisition failed.
+ *
+ * @author Brian S O'Neill
+ */
+public class OptimisticLockException extends PersistException {
+
+ private static final long serialVersionUID = 4081788711829580886L;
+
+ public OptimisticLockException() {
+ super();
+ }
+
+ public OptimisticLockException(String message) {
+ super(message);
+ }
+
+ public OptimisticLockException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public OptimisticLockException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * @param expectedVersion version number that was expected for persistent
+ * record when update was executed
+ */
+ public OptimisticLockException(long expectedVersion) {
+ this((Long) expectedVersion);
+ }
+
+ /**
+ * @param expectedVersion version number that was expected for persistent
+ * record when update was executed
+ */
+ public OptimisticLockException(Object expectedVersion) {
+ this(expectedVersion, null);
+ }
+
+ /**
+ * @param expectedVersion version number that was expected for persistent
+ * record when update was executed
+ * @param savedVersion actual persistent version number of storable
+ */
+ public OptimisticLockException(Object expectedVersion, Object savedVersion) {
+ super(makeMessage(expectedVersion, savedVersion));
+ }
+
+ private static String makeMessage(Object expectedVersion, Object savedVersion) {
+ if (expectedVersion == null) {
+ if (savedVersion == null) {
+ return null;
+ }
+ return "Failed to update because saved version is " + savedVersion;
+ }
+ if (savedVersion == null) {
+ return "Failed to update for expected version " + expectedVersion;
+ }
+ return "Failed to update for expected version " + expectedVersion +
+ " because saved version is " + savedVersion;
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/PersistDeadlockException.java b/src/main/java/com/amazon/carbonado/PersistDeadlockException.java
new file mode 100644
index 0000000..1e807dd
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/PersistDeadlockException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Thrown if a persist operation fails because it was selected to resolve a
+ * deadlock.
+ *
+ * @author Brian S O'Neill
+ */
+public class PersistDeadlockException extends PersistException {
+
+ private static final long serialVersionUID = 1932137921808640293L;
+
+ public PersistDeadlockException() {
+ super();
+ }
+
+ public PersistDeadlockException(String message) {
+ super(message);
+ }
+
+ public PersistDeadlockException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public PersistDeadlockException(Throwable cause) {
+ super(cause);
+ }
+
+ @Override
+ protected FetchException makeFetchException(String message, Throwable cause) {
+ return new FetchDeadlockException(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/PersistDeniedException.java b/src/main/java/com/amazon/carbonado/PersistDeniedException.java
new file mode 100644
index 0000000..f3dd0d6
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/PersistDeniedException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Thrown when a persist operation was denied either because the repository is
+ * in read-only mode, or the connection does not have permission.
+ *
+ * @author Brian S O'Neill
+ */
+public class PersistDeniedException extends PersistException {
+
+ private static final long serialVersionUID = 7235477858113097814L;
+
+ public PersistDeniedException() {
+ super();
+ }
+
+ public PersistDeniedException(String message) {
+ super(message);
+ }
+
+ public PersistDeniedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public PersistDeniedException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/PersistException.java b/src/main/java/com/amazon/carbonado/PersistException.java
new file mode 100644
index 0000000..9436682
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/PersistException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A PersistException is caused by a failure when inserting, updating, or
+ * deleting records from a {@link Repository}.
+ *
+ * @author Brian S O'Neill
+ */
+public class PersistException extends RepositoryException {
+
+ private static final long serialVersionUID = 7900595110884888593L;
+
+ public PersistException() {
+ super();
+ }
+
+ public PersistException(String message) {
+ super(message);
+ }
+
+ public PersistException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public PersistException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/PersistMultipleException.java b/src/main/java/com/amazon/carbonado/PersistMultipleException.java
new file mode 100644
index 0000000..3bae443
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/PersistMultipleException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A PersistMultipleException is thrown when a persist operation would have
+ * applied to more than one record when at most one was expected.
+ *
+ * @author Brian S O'Neill
+ */
+public class PersistMultipleException extends PersistException {
+
+ private static final long serialVersionUID = -7671067829937508160L;
+
+ public PersistMultipleException() {
+ super();
+ }
+
+ public PersistMultipleException(String message) {
+ super(message);
+ }
+
+ public PersistMultipleException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public PersistMultipleException(Throwable cause) {
+ super(cause);
+ }
+
+ @Override
+ protected FetchException makeFetchException(String message, Throwable cause) {
+ return new FetchMultipleException(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/PersistNoneException.java b/src/main/java/com/amazon/carbonado/PersistNoneException.java
new file mode 100644
index 0000000..a6436d2
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/PersistNoneException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A PersistNoneException is thrown when a persist operation applied to no
+ * records when at least one was expected.
+ *
+ * @author Brian S O'Neill
+ */
+public class PersistNoneException extends PersistException {
+
+ private static final long serialVersionUID = -3271539536650048094L;
+
+ public PersistNoneException() {
+ super();
+ }
+
+ public PersistNoneException(String message) {
+ super(message);
+ }
+
+ public PersistNoneException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public PersistNoneException(Throwable cause) {
+ super(cause);
+ }
+
+ @Override
+ protected FetchException makeFetchException(String message, Throwable cause) {
+ return new FetchNoneException(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/PersistTimeoutException.java b/src/main/java/com/amazon/carbonado/PersistTimeoutException.java
new file mode 100644
index 0000000..a1ed6ad
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/PersistTimeoutException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Thrown if a persist operation fails because lock acquisition timed out.
+ *
+ * @author Brian S O'Neill
+ */
+public class PersistTimeoutException extends PersistException {
+ private static final long serialVersionUID = 1L;
+
+ public PersistTimeoutException() {
+ super();
+ }
+
+ public PersistTimeoutException(String message) {
+ super(message);
+ }
+
+ public PersistTimeoutException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public PersistTimeoutException(Throwable cause) {
+ super(cause);
+ }
+
+ @Override
+ protected FetchException makeFetchException(String message, Throwable cause) {
+ return new FetchTimeoutException(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/PrimaryKey.java b/src/main/java/com/amazon/carbonado/PrimaryKey.java
new file mode 100644
index 0000000..ffea9be
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/PrimaryKey.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies a {@link Storable} property as being a member of the primary
+ * key. All Storable types must have at least one property belonging to the
+ * primary key.
+ *
+ * <p>Example:<pre>
+ * <b>&#64;PrimaryKey</b>("userInfoID")
+ * public interface UserInfo extends Storable&lt;UserInfo&gt; {
+ * long getUserInfoID();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @author Brian S O'Neill
+ * @see AlternateKeys
+ * @see Sequence
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface PrimaryKey {
+ /**
+ * A list of property names, which may be prefixed with '+' or '-' to
+ * indicate a preference for ascending or descending order.
+ */
+ String[] value();
+}
diff --git a/src/main/java/com/amazon/carbonado/Query.java b/src/main/java/com/amazon/carbonado/Query.java
new file mode 100644
index 0000000..6073a84
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Query.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.io.IOException;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+/**
+ * Supports complex retrieval and deletion of {@link Storable} objects. A Query
+ * is defined by {@link Storage#query}, and it merely represents an action. It
+ * does not contain any data, and it is immutable. Query objects are usually
+ * compiled and cached, and the same instance can be re-used for future
+ * queries.
+ *
+ * <p>Query instances are thread-safe and immutable. All of the apparent
+ * mutators (with, et al) do not modify the Query, but instead return a new
+ * Query with the requested modification.
+ *
+ * @author Brian S O'Neill
+ */
+public interface Query<S extends Storable> {
+ /**
+ * Returns the specific type of Storable managed by this object.
+ */
+ Class<S> getStorableType();
+
+ /**
+ * Returns the query's filter.
+ */
+ Filter<S> getFilter();
+
+ /**
+ * Returns the query's filter values, which is null if filter has no
+ * parameters.
+ */
+ FilterValues<S> getFilterValues();
+
+ /**
+ * Returns the amount of blank parameters that need to be filled in. If
+ * zero, then this query is ready to be used.
+ */
+ int getBlankParameterCount();
+
+ /**
+ * Returns a copy of this Query with the next blank parameter filled in.
+ *
+ * @param value parameter value to fill in
+ * @throws IllegalStateException if no blank parameters
+ * @throws IllegalArgumentException if type doesn't match
+ */
+ Query<S> with(int value);
+
+ /**
+ * Returns a copy of this Query with the next blank parameter filled in.
+ *
+ * @param value parameter value to fill in
+ * @throws IllegalStateException if no blank parameters
+ * @throws IllegalArgumentException if type doesn't match
+ */
+ Query<S> with(long value);
+
+ /**
+ * Returns a copy of this Query with the next blank parameter filled in.
+ *
+ * @param value parameter value to fill in
+ * @throws IllegalStateException if no blank parameters
+ * @throws IllegalArgumentException if type doesn't match
+ */
+ Query<S> with(float value);
+
+ /**
+ * Returns a copy of this Query with the next blank parameter filled in.
+ *
+ * @param value parameter value to fill in
+ * @throws IllegalStateException if no blank parameters
+ * @throws IllegalArgumentException if type doesn't match
+ */
+ Query<S> with(double value);
+
+ /**
+ * Returns a copy of this Query with the next blank parameter filled in.
+ *
+ * @param value parameter value to fill in
+ * @throws IllegalStateException if no blank parameters
+ * @throws IllegalArgumentException if type doesn't match
+ */
+ Query<S> with(boolean value);
+
+ /**
+ * Returns a copy of this Query with the next blank parameter filled in.
+ *
+ * @param value parameter value to fill in
+ * @throws IllegalStateException if no blank parameters
+ * @throws IllegalArgumentException if type doesn't match
+ */
+ Query<S> with(char value);
+
+ /**
+ * Returns a copy of this Query with the next blank parameter filled in.
+ *
+ * @param value parameter value to fill in
+ * @throws IllegalStateException if no blank parameters
+ * @throws IllegalArgumentException if type doesn't match
+ */
+ Query<S> with(byte value);
+
+ /**
+ * Returns a copy of this Query with the next blank parameter filled in.
+ *
+ * @param value parameter value to fill in
+ * @throws IllegalStateException if no blank parameters
+ * @throws IllegalArgumentException if type doesn't match
+ */
+ Query<S> with(short value);
+
+ /**
+ * Returns a copy of this Query with the next blank parameter filled in.
+ *
+ * @param value parameter value to fill in
+ * @throws IllegalStateException if no blank parameters
+ * @throws IllegalArgumentException if type doesn't match
+ */
+ Query<S> with(Object value);
+
+ /**
+ * Returns a copy of this Query with the next blank parameters filled in.
+ *
+ * @param values parameter values to fill in; if null or empty, this
+ * Query instance is returned
+ * @throws IllegalStateException if no blank parameters or if too many
+ * parameter values supplied
+ * @throws IllegalArgumentException if any type doesn't match
+ */
+ Query<S> withValues(Object... values);
+
+ /**
+ * Returns a new query which has another {@link Storage#query(String)
+ * filter} logically "and"ed to this, potentially reducing the amount of
+ * results.
+ *
+ * @param filter query filter expression
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalStateException if any blank parameters in this query, or
+ * if this query is already guaranteed to fetch nothing
+ * @throws IllegalArgumentException if filter is null
+ * @throws MalformedFilterException if expression is malformed
+ * @throws UnsupportedOperationException if given filter is unsupported by repository
+ */
+ Query<S> and(String filter) throws FetchException;
+
+ /**
+ * Returns a new query which has another {@link Storage#query(String)
+ * filter} logically "and"ed to this, potentially reducing the amount of
+ * results.
+ *
+ * @param filter query filter
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalStateException if any blank parameters in this query, or
+ * if this query is already guaranteed to fetch nothing
+ * @throws IllegalArgumentException if filter is null
+ * @throws UnsupportedOperationException if given filter is unsupported by repository
+ */
+ Query<S> and(Filter<S> filter) throws FetchException;
+
+ /**
+ * Returns a new query which has another {@link Storage#query(String)
+ * filter} logically "or"ed to this, potentially increasing the amount of
+ * results.
+ *
+ * @param filter query filter expression
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalStateException if any blank parameters in this query, or
+ * if this query is already guaranteed to fetch everything
+ * @throws IllegalArgumentException if filter is null
+ * @throws MalformedFilterException if expression is malformed
+ * @throws UnsupportedOperationException if given filter is unsupported by repository
+ */
+ Query<S> or(String filter) throws FetchException;
+
+ /**
+ * Returns a new query which has another {@link Storage#query(String)
+ * filter} logically "or"ed to this, potentially increasing the amount of
+ * results.
+ *
+ * @param filter query filter
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalStateException if any blank parameters in this query, or
+ * if this query is already guaranteed to fetch everything
+ * @throws IllegalArgumentException if filter is null
+ * @throws UnsupportedOperationException if given filter is unsupported by repository
+ */
+ Query<S> or(Filter<S> filter) throws FetchException;
+
+ /**
+ * Returns a new query which produces all the results not supplied in this
+ * query. Any filled in parameters in this query are copied into the new
+ * one.
+ *
+ * @throws FetchException if storage layer throws an exception
+ * @throws UnsupportedOperationException if new query is unsupported by repository
+ */
+ Query<S> not() throws FetchException;
+
+ /**
+ * Returns a copy of this query ordered by a specific property value. The
+ * property name may be prefixed with '+' or '-' to indicate ascending or
+ * descending order. If the prefix is omitted, ascending order is assumed.
+ *
+ * <p>Note: Specification of ordering properties is not cumulative. Calling
+ * this method will first remove any previous ordering properties.
+ *
+ * @param property name of property to order by
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalArgumentException if property is null or is not a member
+ * of type S
+ * @throws UnsupportedOperationException if given ordering, combined with
+ * query filter, is unsupported by repository
+ */
+ Query<S> orderBy(String property) throws FetchException;
+
+ /**
+ * Returns a copy of this query ordered by specific property values. The
+ * property names may be prefixed with '+' or '-' to indicate ascending or
+ * descending order. If the prefix is omitted, ascending order is assumed.
+ *
+ * <p>Note: Specification of ordering properties is not cumulative. Calling
+ * this method will first remove any previous ordering properties.
+ *
+ * @param properties names of properties to order by
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalArgumentException if any property is null or is not a
+ * member of type S
+ * @throws UnsupportedOperationException if given ordering, combined with
+ * query filter, is unsupported by repository
+ */
+ Query<S> orderBy(String... properties) throws FetchException;
+
+ /**
+ * Fetches results for this query. If any updates or deletes might be
+ * performed on the results, consider enclosing the fetch in a
+ * transaction. This allows the isolation level and "for update" mode to be
+ * adjusted. Some repositories might otherwise deadlock.
+ *
+ * @return fetch results
+ * @throws IllegalStateException if any blank parameters in this query
+ * @throws FetchException if storage layer throws an exception
+ * @see Repository#enterTransaction(IsolationLevel)
+ */
+ Cursor<S> fetch() throws FetchException;
+
+ /**
+ * Fetches results for this query after a given starting point, which is
+ * useful for re-opening a cursor. This is only effective when query has
+ * been given an explicit {@link #orderBy ordering}. If not a total
+ * ordering, then returned cursor may start at an earlier position.
+ *
+ * <p>Note: This method can be very expensive to call repeatedly, if the
+ * query needs to perform a sort operation. Ideally, the query ordering
+ * should match the natural ordering of an index or key.
+ *
+ * @param start storable to attempt to start after; if null, fetch all results
+ * @return fetch results
+ * @throws IllegalStateException if any blank parameters in this query
+ * @throws FetchException if storage layer throws an exception
+ * @see Repository#enterTransaction(IsolationLevel)
+ */
+ Cursor<S> fetchAfter(S start) throws FetchException;
+
+ /**
+ * Attempts to load exactly one matching object. If the number of matching
+ * records is zero or exceeds one, then an exception is thrown instead.
+ *
+ * @return a single fetched object
+ * @throws IllegalStateException if any blank parameters in this query
+ * @throws FetchNoneException if no matching record found
+ * @throws FetchMultipleException if more than one matching record found
+ * @throws FetchException if storage layer throws an exception
+ */
+ S loadOne() throws FetchException;
+
+ /**
+ * May return null if nothing found. Throws exception if record count is
+ * more than one.
+ *
+ * @return null or a single fetched object
+ * @throws IllegalStateException if any blank parameters in this query
+ * @throws FetchMultipleException if more than one matching record found
+ * @throws FetchException if storage layer throws an exception
+ */
+ S tryLoadOne() throws FetchException;
+
+ /**
+ * Deletes one matching object. If the number of matching records is zero or
+ * exceeds one, then no delete occurs, and an exception is thrown instead.
+ *
+ * @throws IllegalStateException if any blank parameters in this query
+ * @throws PersistNoneException if no matching record found
+ * @throws PersistMultipleException if more than one record matches
+ * @throws PersistException if storage layer throws an exception
+ */
+ void deleteOne() throws PersistException;
+
+ /**
+ * Deletes zero or one matching objects. If the number of matching records
+ * exceeds one, then no delete occurs, and an exception is thrown instead.
+ *
+ * @return true if record existed and was deleted, or false if no match
+ * @throws IllegalStateException if any blank parameters in this query
+ * @throws PersistMultipleException if more than one record matches
+ * @throws PersistException if storage layer throws an exception
+ */
+ boolean tryDeleteOne() throws PersistException;
+
+ /**
+ * Deletes zero or more matching objects. There is no guarantee that
+ * deleteAll is an atomic operation. If atomic behavior is desired, wrap
+ * the call in a transaction scope.
+ *
+ * @throws IllegalStateException if any blank parameters in this query
+ * @throws PersistException if storage layer throws an exception
+ */
+ void deleteAll() throws PersistException;
+
+ /**
+ * Returns a count of all results matched by this query. Even though no
+ * results are explicitly fetched, this method may still be expensive to
+ * call. The actual performance will vary by repository and available indexes.
+ *
+ * @return count of matches
+ * @throws IllegalStateException if any blank parameters in this query
+ * @throws FetchException if storage layer throws an exception
+ */
+ long count() throws FetchException;
+
+ /**
+ * Print the native query to standard out, which is useful for performance
+ * analysis. Not all repositories have a native query format. An example
+ * native format is SQL.
+ *
+ * @return false if not implemented
+ */
+ boolean printNative();
+
+ /**
+ * Prints the native query to any appendable, which is useful for
+ * performance analysis. Not all repositories have a native query
+ * format. An example native format is SQL.
+ *
+ * @param app append results here
+ * @return false if not implemented
+ */
+ boolean printNative(Appendable app) throws IOException;
+
+ /**
+ * Prints the native query to any appendable, which is useful for
+ * performance analysis. Not all repositories have a native query
+ * format. An example native format is SQL.
+ *
+ * @param app append results here
+ * @param indentLevel amount to indent text, zero for none
+ * @return false if not implemented
+ */
+ boolean printNative(Appendable app, int indentLevel) throws IOException;
+
+ /**
+ * Prints the query excecution plan to standard out, which is useful for
+ * performance analysis. There is no standard format for query plans, nor
+ * is it a requirement that this method be implemented.
+ *
+ * @return false if not implemented
+ */
+ boolean printPlan();
+
+ /**
+ * Prints the query excecution plan to any appendable, which is useful for
+ * performance analysis. There is no standard format for query plans, nor
+ * is it a requirement that this method be implemented.
+ *
+ * @param app append results here
+ * @return false if not implemented
+ */
+ boolean printPlan(Appendable app) throws IOException;
+
+ /**
+ * Prints the query excecution plan to any appendable, which is useful for
+ * performance analysis. There is no standard format for query plans, nor
+ * is it a requirement that this method be implemented.
+ *
+ * @param app append results here
+ * @param indentLevel amount to indent text, zero for none
+ * @return false if not implemented
+ */
+ boolean printPlan(Appendable app, int indentLevel) throws IOException;
+
+ int hashCode();
+
+ boolean equals(Object obj);
+
+ /**
+ * Returns a description of the query filter and any other arguments.
+ */
+ String toString();
+}
diff --git a/src/main/java/com/amazon/carbonado/Repository.java b/src/main/java/com/amazon/carbonado/Repository.java
new file mode 100644
index 0000000..7128fea
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Repository.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import com.amazon.carbonado.capability.Capability;
+
+/**
+ * A Repository represents a database for {@link Storable}
+ * instances. Some repositories do not have control over the schema (for example, a JDBC
+ * Repository depends on the schema defined by the underlying relational database); such
+ * repositories are called "dependent". Conversely, a repository which has complete control
+ * over the schema is termed "independent".
+ *
+ * <P>A dependent repository requires and will verify that Storables
+ * have a matching definition in the external storage layer. An independent
+ * repository will automatically update type definitions in its database to
+ * match changes to Storable definitions.
+ *
+ * <p>Repository instances should be thread-safe and immutable. Therefore, it
+ * is safe for multiple threads to be interacting with a Repository.
+ *
+ * @author Brian S O'Neill
+ * @see RepositoryBuilder
+ */
+public interface Repository {
+ /**
+ * Returns the name of this repository.
+ */
+ String getName();
+
+ /**
+ * Returns a Storage instance for the given user defined Storable class or
+ * interface.
+ *
+ * @return specific type of Storage instance
+ * @throws IllegalArgumentException if specified type is null
+ * @throws MalformedTypeException if specified type is not suitable
+ * @throws SupportException if specified type cannot be supported
+ * @throws RepositoryException if storage layer throws any other kind of
+ * exception
+ */
+ <S extends Storable> Storage<S> storageFor(Class<S> type)
+ throws SupportException, RepositoryException;
+
+ /**
+ * Causes the current thread to enter a transaction scope. Call commit
+ * inside the transaction in order for any updates to the repository to be
+ * applied. Be sure to call exit when leaving the scope.
+ * <p>
+ * To ensure exit is called, use transactions as follows:
+ * <pre>
+ * Transaction txn = repository.enterTransaction();
+ * try {
+ * // Make updates to storage layer
+ * ...
+ *
+ * // Commit the changes up to this point
+ * txn.commit();
+ *
+ * // Optionally make more updates
+ * ...
+ *
+ * // Commit remaining changes
+ * txn.commit();
+ * } finally {
+ * // Ensure transaction exits, aborting uncommitted changes if an exception was thrown
+ * txn.exit();
+ * }
+ * </pre>
+ */
+ Transaction enterTransaction();
+
+ /**
+ * Causes the current thread to enter a transaction scope with an explict
+ * isolation level. The actual isolation level may be higher than
+ * requested, if the repository does not support the exact level. If the
+ * repository does not support a high enough level, it throws an
+ * UnsupportedOperationException.
+ *
+ * @param level minimum desired transaction isolation level -- if null, a
+ * suitable default is selected
+ * @see #enterTransaction()
+ * @throws UnsupportedOperationException if repository does not support
+ * isolation as high as the desired level
+ */
+ Transaction enterTransaction(IsolationLevel level);
+
+ /**
+ * Causes the current thread to enter a <i>top-level</i> transaction scope
+ * with an explict isolation level. The actual isolation level may be
+ * higher than requested, if the repository does not support the exact
+ * level. If the repository does not support a high enough level, it throws
+ * an UnsupportedOperationException.
+ *
+ * <p>This method requests a top-level transaction, which means it never
+ * has a parent transaction, but it still can be a parent transaction
+ * itself. This kind of transaction is useful when a commit must absolutely
+ * succeed, even if the current thread is already in a transaction
+ * scope. If there was a parent transaction, then a commit might still be
+ * rolled back by the parent.
+ *
+ * <p>Requesting a top-level transaction can be deadlock prone if the
+ * current thread is already in a transaction scope. The top-level
+ * transaction may not be able to obtain locks held by the parent
+ * transaction. An alternative to requesting top-level transactions is to
+ * execute transactions in separate threads.
+ *
+ * @param level minimum desired transaction isolation level -- if null, a
+ * suitable default is selected
+ * @see #enterTransaction()
+ * @throws UnsupportedOperationException if repository does not support
+ * isolation as high as the desired level
+ */
+ Transaction enterTopTransaction(IsolationLevel level);
+
+ /**
+ * Returns the isolation level of the current transaction, or null if there
+ * is no transaction in the current thread.
+ */
+ IsolationLevel getTransactionIsolationLevel();
+
+ /**
+ * Requests a specific capability of this Repository. This allows
+ * repositories to support extended features without having to clutter the
+ * main repository interface. The list of supported capabilities is
+ * documented with repository implementations.
+ *
+ * @param capabilityType type of capability requested
+ * @return capability instance or null if not supported
+ */
+ <C extends Capability> C getCapability(Class<C> capabilityType);
+
+ /**
+ * Closes this repository reference, aborting any current
+ * transactions. Operations on objects returned by this repository will
+ * fail when accessing the storage layer.
+ *
+ * @throws SecurityException if caller does not have permission
+ */
+ void close();
+}
diff --git a/src/main/java/com/amazon/carbonado/RepositoryBuilder.java b/src/main/java/com/amazon/carbonado/RepositoryBuilder.java
new file mode 100644
index 0000000..4b1d3fe
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/RepositoryBuilder.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Standard interface for building up configuration and opening a {@link
+ * Repository} instance. All repository implementations should be constructable
+ * via a builder that implements this interface. Builders should follow a
+ * pattern where configuration is supplied via property access methods. With
+ * this design, each item can have extensive documentation and optional
+ * configuration can be ignored.
+ *
+ * <p>A builder design also offers advantages over constructors in that a
+ * different repository can be built depending on the specific
+ * configuration. This logic is hidden, making it easier to use repositories
+ * that would otherwise require complex steps to construct.
+ *
+ * @author Brian S O'Neill
+ */
+public interface RepositoryBuilder {
+ /**
+ * Builds a repository instance.
+ *
+ * @throws ConfigurationException if there is a problem in the builder's configuration
+ * @throws RepositoryException if there is a general problem opening the repository
+ */
+ Repository build() throws ConfigurationException, RepositoryException;
+
+ /**
+ * Builds a repository instance.
+ *
+ * <p>If the repository is being wrapped by a parent repository, the child
+ * repository will need to know this fact for some operations to work
+ * correctly. Since the parent repository is not built yet, a reference is
+ * used instead.
+ *
+ * @param rootReference reference to root parent repository, to be set by
+ * parent repository upon being built
+ * @throws ConfigurationException if there is a problem in the builder's configuration
+ * @throws RepositoryException if there is a general problem opening the repository
+ */
+ Repository build(RepositoryReference rootReference)
+ throws ConfigurationException, RepositoryException;
+
+ /**
+ * Returns the name of the repository.
+ */
+ String getName();
+
+ /**
+ * Set name for the repository, which is required.
+ */
+ void setName(String name);
+
+ /**
+ * Returns true if repository should assume the role of master, which is
+ * true by default. Repositories that link different repositories together
+ * will designate only one as the master.
+ *
+ * <p>A master repository is responsible for {@link Version version} and
+ * {@link Sequence sequence} properties. For insert operations, a master
+ * repository must set these properties if they are uninitialized. For
+ * updates, the version property is checked to see if an {@link
+ * OptimisticLockException} should be thrown.
+ *
+ * @see com.amazon.carbonado.repo.replicated.ReplicatedRepositoryBuilder
+ */
+ boolean isMaster();
+
+ /**
+ * Set to false if repository should not assume the role of master. By
+ * default, this option is true. Repositories that link different
+ * repositories together will designate only one as the master.
+ *
+ * <p>A master repository is responsible for {@link Version version} and
+ * {@link Sequence sequence} properties. For insert operations, a master
+ * repository must set these properties if they are uninitialized. For
+ * updates, the version property is checked to see if an {@link
+ * OptimisticLockException} should be thrown.
+ *
+ * @see com.amazon.carbonado.repo.replicated.ReplicatedRepositoryBuilder
+ */
+ void setMaster(boolean b);
+
+ public class RepositoryReference extends AtomicReference<Repository> {}
+}
diff --git a/src/main/java/com/amazon/carbonado/RepositoryException.java b/src/main/java/com/amazon/carbonado/RepositoryException.java
new file mode 100644
index 0000000..e6887b6
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/RepositoryException.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.util.Random;
+
+/**
+ * General checked exception thrown when accessing a {@link Repository}.
+ *
+ * <p>Some repository exceptions are the result of an optimistic lock failure
+ * or deadlock. One resolution strategy is to exit all transactions and try the
+ * operation again, after waiting some bounded random amount of time. As a
+ * convenience, this class provides a mechanism to support such a backoff
+ * strategy. For example:
+ *
+ * <pre>
+ * // Retry at most three more times
+ * for (int retryCount = 3;;) {
+ * try {
+ * ...
+ * myObject.load();
+ * ...
+ * myObject.update();
+ * break;
+ * } catch (OptimisticLockException e) {
+ * // Wait up to one second before retrying
+ * retryCount = e.backoff(e, retryCount, 1000);
+ * }
+ * }
+ * </pre>
+ *
+ * If the retry count is zero (or less) when backoff is called, then the
+ * original exception is rethrown, indicating retry failure.
+ *
+ * @author Brian S O'Neill
+ */
+public class RepositoryException extends Exception {
+
+ private static final long serialVersionUID = 7261406895435249366L;
+
+ /**
+ * One strategy for resolving an optimistic lock failure is to try the
+ * operation again, after waiting some bounded random amount of time. This
+ * method is provided as a convenience, to support such a random wait.
+ * <p>
+ * A retry count is required as well, which is decremented and returned by
+ * this method. If the retry count is zero (or less) when this method is
+ * called, then this exception is thrown again, indicating retry failure.
+ *
+ * @param retryCount current retry count, if zero, throw this exception again
+ * @param milliseconds upper bound on the random amount of time to wait
+ * @return retryCount minus one
+ * @throws E if retry count is zero
+ */
+ public static <E extends Throwable> int backoff(E e, int retryCount, int milliseconds)
+ throws E
+ {
+ if (retryCount <= 0) {
+ // Workaround apparent compiler bug.
+ com.amazon.carbonado.util.ThrowUnchecked.fire(e);
+ }
+ if (milliseconds > 0) {
+ Random rnd = cRandom;
+ if (rnd == null) {
+ cRandom = rnd = new Random();
+ }
+ if ((milliseconds = rnd.nextInt(milliseconds)) > 0) {
+ try {
+ Thread.sleep(milliseconds);
+ } catch (InterruptedException e2) {
+ }
+ return retryCount - 1;
+ }
+ }
+ Thread.yield();
+ return retryCount - 1;
+ }
+
+ private static Random cRandom;
+
+ public RepositoryException() {
+ super();
+ }
+
+ public RepositoryException(String message) {
+ super(message);
+ }
+
+ public RepositoryException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public RepositoryException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Recursively calls getCause, until the root cause is found. Returns this
+ * if no root cause.
+ */
+ public Throwable getRootCause() {
+ Throwable cause = this;
+ while (cause.getCause() != null) {
+ cause = cause.getCause();
+ }
+ return cause;
+ }
+
+ /**
+ * Converts RepositoryException into an appropriate PersistException.
+ */
+ public final PersistException toPersistException() {
+ return toPersistException(null);
+ }
+
+ /**
+ * Converts RepositoryException into an appropriate PersistException, prepending
+ * the specified message. If message is null, original exception message is
+ * preserved.
+ *
+ * @param message message to prepend, which may be null
+ */
+ public final PersistException toPersistException(final String message) {
+ Throwable cause;
+ if (this instanceof PersistException) {
+ cause = this;
+ } else {
+ cause = getCause();
+ }
+
+ if (cause == null) {
+ cause = this;
+ } else if (cause instanceof PersistException && message == null) {
+ return (PersistException) cause;
+ }
+
+ String causeMessage = cause.getMessage();
+ if (causeMessage == null) {
+ causeMessage = message;
+ } else if (message != null) {
+ causeMessage = message + " : " + causeMessage;
+ }
+
+ return makePersistException(causeMessage, cause);
+ }
+
+ /**
+ * Converts RepositoryException into an appropriate FetchException.
+ */
+ public final FetchException toFetchException() {
+ return toFetchException(null);
+ }
+
+ /**
+ * Converts RepositoryException into an appropriate FetchException, prepending
+ * the specified message. If message is null, original exception message is
+ * preserved.
+ *
+ * @param message message to prepend, which may be null
+ */
+ public final FetchException toFetchException(final String message) {
+ Throwable cause;
+ if (this instanceof FetchException) {
+ cause = this;
+ } else {
+ cause = getCause();
+ }
+
+ if (cause == null) {
+ cause = this;
+ } else if (cause instanceof FetchException && message == null) {
+ return (FetchException) cause;
+ }
+
+ String causeMessage = cause.getMessage();
+ if (causeMessage == null) {
+ causeMessage = message;
+ } else if (message != null) {
+ causeMessage = message + " : " + causeMessage;
+ }
+
+ return makeFetchException(causeMessage, cause);
+ }
+
+ /**
+ * Subclasses can override this to provide a more specialized exception.
+ *
+ * @param message exception message, which may be null
+ * @param cause non-null cause
+ */
+ protected PersistException makePersistException(String message, Throwable cause) {
+ return new PersistException(message, cause);
+ }
+
+ /**
+ * Subclasses can override this to provide a more specialized exception.
+ *
+ * @param message exception message, which may be null
+ * @param cause non-null cause
+ */
+ protected FetchException makeFetchException(String message, Throwable cause) {
+ return new FetchException(message, cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/Sequence.java b/src/main/java/com/amazon/carbonado/Sequence.java
new file mode 100644
index 0000000..ea38493
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Sequence.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies a {@link Storable} property capable of selecting its own value
+ * on insert, by a named sequence. Sequences are supported at the storage
+ * layer, and implementions that do not support sequences ignore this
+ * annotation.
+ *
+ * <p>Example:<pre>
+ * &#64;PrimaryKey("userInfoID")
+ * public interface UserInfo extends Storable&lt;UserInfo&gt; {
+ * <b>&#64;Sequence("USER_ID_SEQ")</b>
+ * long getUserInfoID();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @author Brian S O'Neill
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Sequence {
+ /**
+ * Name of the sequence used by the storage layer.
+ */
+ String value();
+}
diff --git a/src/main/java/com/amazon/carbonado/Storable.java b/src/main/java/com/amazon/carbonado/Storable.java
new file mode 100644
index 0000000..f5c2248
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Storable.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A data access object in a {@link Repository}. User defined storables must
+ * either extend or implement this interface via an interface or abstract
+ * class. Bean properties defined in the storable are persisted into the
+ * repository. At least one property must be annotated as the {@link
+ * PrimaryKey}. At most one property may be annotated as being the {@link
+ * Version} property.
+ *
+ * <p>Storable instances are mutable, but they must be thread-safe. Although
+ * there may be race conditions if multiple threads are mutating the Storable,
+ * the Storable instance will not get into a corrupt state.
+ *
+ * @author Brian S O'Neill
+ * @author Don Schneider
+ *
+ * @see com.amazon.carbonado.Alias
+ * @see com.amazon.carbonado.Indexes
+ * @see com.amazon.carbonado.Join
+ * @see com.amazon.carbonado.Nullable
+ * @see com.amazon.carbonado.PrimaryKey
+ * @see com.amazon.carbonado.Version
+ */
+public interface Storable<S extends Storable<S>> {
+ /**
+ * Loads or reloads this object from the storage layer by its primary
+ * key. This object's primary key itself is never modified by calling
+ * load. If load is successful, altering the primary key is no longer
+ * allowed unless the object is deleted. Attempting to alter the primary
+ * key in this state results in an {@link IllegalStateException}.
+ *
+ * <p>Note: This method differs from {@link #tryLoad} only in that it
+ * throws an exception if no matching record was found instead of returning
+ * false. This may indicate that the underlying record was deleted between
+ * a load and reload. When a FetchNoneException is thrown, this object's
+ * state will be the same as if the delete method was called on it.
+ *
+ * @throws FetchNoneException if no matching record found
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalStateException if the state of this instance suggests
+ * that any primary keys are unspecified
+ */
+ void load() throws FetchNoneException, FetchException;
+
+ /**
+ * Loads or reloads this object from the storage layer by its primary
+ * key. This object's primary key itself is never modified by calling
+ * load. If load is successful, altering the primary key is no longer
+ * allowed unless the object is deleted. Attempting to alter the primary
+ * key in this state results in an {@link IllegalStateException}.
+ *
+ * <p>Note: This method differs from {@link #load} only in that it returns
+ * false if no matching record was found instead of throwing an exception.
+ * This may indicate that the underlying record was deleted between a load
+ * and reload. When false is returned, this object's state will be the same
+ * as if the delete method was called on it.
+ *
+ * @return true if found and loaded, false otherwise
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalStateException if the state of this instance suggests
+ * that any primary keys are unspecified
+ */
+ boolean tryLoad() throws FetchException;
+
+ /**
+ * Inserts a new persistent value for this object. If successful, altering
+ * the primary key is no longer allowed unless the object is deleted.
+ * Attempting to alter the primary key in this state results in an
+ * {@link IllegalStateException}.
+ *
+ * <p>Insert requires that all primary key properties be specified. If not,
+ * an {@link IllegalStateException} is thrown. Also, repository
+ * implementations usually require that properties which are not {@link
+ * Nullable} also be specified. Otherwise, a {@link ConstraintException}
+ * may be thrown.
+ *
+ * <p>Note: This method differs from {@link #tryInsert} only in that it may
+ * throw a UniqueConstraintException instead of returning false.
+ *
+ * @throws UniqueConstraintException if it is absolutely known that a key
+ * of inserted object matches an existing one
+ * @throws ConstraintException if any required properties are unspecified
+ * @throws PersistException if storage layer throws an exception
+ * @throws IllegalStateException if the state of this instance suggests
+ * that any primary keys are unspecified
+ */
+ void insert() throws PersistException;
+
+ /**
+ * Inserts a new persistent value for this object. If successful, altering
+ * the primary key is no longer allowed unless the object is deleted.
+ * Attempting to alter the primary key in this state results in an
+ * {@link IllegalStateException}.
+ *
+ * <p>Insert requires that all primary key properties be specified. If not,
+ * an {@link IllegalStateException} is thrown. Also, repository
+ * implementations usually require that properties which are not {@link
+ * Nullable} also be specified. Otherwise, a {@link ConstraintException}
+ * may be thrown.
+ *
+ * <p>Note: This method differs from {@link #insert} only in that it
+ * returns false instead of throwing a UniqueConstraintException.
+ *
+ * @return false if it is absolutely known that a key of inserted object
+ * matches an existing one
+ * @throws ConstraintException if any required properties are unspecified
+ * @throws PersistException if storage layer throws an exception
+ * @throws IllegalStateException if the state of this instance suggests
+ * that any primary keys are unspecified
+ */
+ boolean tryInsert() throws PersistException;
+
+ /**
+ * Updates the persistent value of this object, regardless of whether this
+ * object has actually been loaded or not. If successful, altering the
+ * primary key is no longer allowed unless the object is deleted.
+ * Attempting to alter the primary key in this state results in an
+ * {@link IllegalStateException}.
+ *
+ * <p>If this object has a {@link Version version} property defined, then
+ * the update logic is a bit more strict. Updates of any storable require
+ * that the primary keys be specified; if a version is present, the version
+ * must be specified as well. If any of the primary key or version
+ * properties are unspecified, an {@link IllegalStateException} will be
+ * thrown; if they are fully specified and the version doesn't match the
+ * current record, an {@link OptimisticLockException} is thrown.
+ *
+ * <p>Not all properties need to be set on this object when calling
+ * update. Setting a subset results in a partial update. After a successful
+ * update, all properties are set to the actual values in the storage
+ * layer. Put another way, the object is automatically reloaded after a
+ * successful update.
+ *
+ * <p>If PersistNoneException is thrown, this indicates that the underlying
+ * record was deleted. When this happens, this object's state will be the
+ * same as if the delete method was called on it.
+ *
+ * @throws PersistNoneException if record is missing and no update occurred
+ * @throws PersistException if storage layer throws an exception
+ * @throws OptimisticLockException if a version property exists and the
+ * optimistic lock failed
+ * @throws IllegalStateException if the state of this instance suggests
+ * that any primary keys are unspecified, or if a version property is unspecified
+ */
+ void update() throws PersistException;
+
+ /**
+ * Updates the persistent value of this object, regardless of whether this
+ * object has actually been loaded or not. If successful, altering the
+ * primary key is no longer allowed unless the object is deleted.
+ * Attempting to alter the primary key in this state results in an
+ * {@link IllegalStateException}.
+ *
+ * <p>If this object has a {@link Version version} property defined, then
+ * the update logic is a bit more strict. Updates of any storable require
+ * that the primary keys be specified; if a version is present, the version
+ * must be specified as well. If any of the primary key or version
+ * properties are unspecified, an {@link IllegalStateException} will be
+ * thrown; if they are fully specified and the version doesn't match the
+ * current record, an {@link OptimisticLockException} is thrown.
+ *
+ * <p>Not all properties need to be set on this object when calling
+ * update. Setting a subset results in a partial update. After a successful
+ * update, all properties are set to the actual values in the storage
+ * layer. Put another way, the object is automatically reloaded after a
+ * successful update.
+ *
+ * <p>A return value of false indicates that the underlying record was
+ * deleted. When this happens, this object's state will be the same as if
+ * the delete method was called on it.
+ *
+ * @return true if record likely exists and was updated, or false if record
+ * absolutely no longer exists and no update occurred
+ * @throws PersistException if storage layer throws an exception
+ * @throws OptimisticLockException if a version property exists and the
+ * optimistic lock failed
+ * @throws IllegalStateException if the state of this instance suggests
+ * that any primary keys are unspecified, or if a version property is unspecified
+ */
+ boolean tryUpdate() throws PersistException;
+
+ /**
+ * Deletes this object from the storage layer by its primary key,
+ * regardless of whether this object has actually been loaded or not.
+ * Calling delete does not prevent this object from being used again. All
+ * property values are still valid, including the primary key. Once
+ * deleted, the insert operation is permitted again.
+ *
+ * <p>Note: This method differs from {@link #tryDelete} only in that it may
+ * throw a PersistNoneException instead of returning false.
+ *
+ * @throws PersistNoneException if record is missing and nothing was
+ * deleted
+ * @throws PersistException if storage layer throws an exception
+ * @throws IllegalStateException if the state of this instance suggests
+ * that any primary keys are unspecified
+ */
+ void delete() throws PersistException;
+
+ /**
+ * Deletes this object from the storage layer by its primary key,
+ * regardless of whether this object has actually been loaded or not.
+ * Calling delete does not prevent this object from being used again. All
+ * property values are still valid, including the primary key. Once
+ * deleted, the insert operation is permitted again.
+ *
+ * <p>Note: This method differs from {@link #delete} only in that it
+ * returns false instead of throwing a PersistNoneException.
+ *
+ * @return true if record likely existed and was deleted, or false if record
+ * absolutely no longer exists and no delete was necessary
+ * @throws PersistException if storage layer throws an exception
+ * @throws IllegalStateException if the state of this instance suggests
+ * that any primary keys are unspecified
+ */
+ boolean tryDelete() throws PersistException;
+
+ /**
+ * Returns the class or interface from which this storable was
+ * generated. This represents the data class for the storable.
+ *
+ * <p><i>Design note: the name "getStorableType" is avoided, so as not to
+ * conflict with a user defined property of "storableType"</i>
+ */
+ Class<S> storableType();
+
+ /**
+ * Copies all supported properties, skipping any that are uninitialized.
+ * Specifically, calls "target.set&lt;property&gt;" for all supported
+ * properties in this storable, passing the value of the property from this
+ * object. Unsupported {@link Independent independent} properties in this
+ * or the target are not copied.
+ *
+ * @param target storable on which to call set&lt;property&gt; methods
+ * @throws IllegalStateException if any primary key properties of target
+ * cannot be altered
+ */
+ void copyAllProperties(S target);
+
+ /**
+ * Copies all supported primary key properties, skipping any that are
+ * uninitialized. Specifically, calls "target.set&lt;property&gt;" for all
+ * supported properties which participate in the primary key, passing the
+ * value of the property from this object. Unsupported {@link Independent
+ * independent} properties in this or the target are not copied.
+ *
+ * @param target storable on which to call set&lt;property&gt; methods
+ * @throws IllegalStateException if any primary key properties of target
+ * cannot be altered
+ */
+ void copyPrimaryKeyProperties(S target);
+
+ /**
+ * Copies the optional version property, unless it is uninitialized.
+ * Specifically, calls "target.set&lt;property&gt;" for the version
+ * property (if supported), passing the value of the property from this
+ * object. If no version property is defined, then this method does
+ * nothing. Unsupported {@link Independent independent} properties in this
+ * or the target are not copied.
+ *
+ * @param target storable on which to call set&lt;property&gt; method
+ */
+ void copyVersionProperty(S target);
+
+ /**
+ * Copies all supported non-primary key properties which are unequal,
+ * skipping any that are uninitialized. Specifically, calls
+ * "target.get&lt;property&gt;", and if the value thus retrieved differs
+ * from the local value, "target.set&lt;property&gt;" is called for that
+ * property. Unsupported {@link Independent independent} properties in this
+ * or the target are not copied.
+ *
+ * @param target storable on which to call set&lt;property&gt; methods
+ */
+ void copyUnequalProperties(S target);
+
+ /**
+ * Copies all supported non-primary key properties which are
+ * dirty. Specifically, calls "target.set&lt;property&gt;" for any
+ * non-primary key property which is dirty, passing the value of the
+ * property from this object. A property is considered dirty when set
+ * before a load or persist operation is called. Unsupported {@link
+ * Independent independent} properties in this or the target are not
+ * copied.
+ *
+ * @param target storable on which to call set&lt;property&gt; methods
+ */
+ void copyDirtyProperties(S target);
+
+ /**
+ * Returns true if any non-primary key properties in this object are
+ * dirty. A property is considered dirty when set before a load or persist
+ * operation is called. A property becomes clean after a successful load,
+ * insert, or update operation.
+ */
+ boolean hasDirtyProperties();
+
+ /**
+ * Marks all dirty properties as clean. Uninitialized properties remain so.
+ * As a side-effect, initialized primary keys may no longer be altered.
+ */
+ void markPropertiesClean();
+
+ /**
+ * Marks all properties as clean, including uninitialized properties.
+ * As a side-effect, primary keys may no longer be altered.
+ */
+ void markAllPropertiesClean();
+
+ /**
+ * Marks all clean properties as dirty. Uninitialized properties remain so.
+ * As a side-effect, primary keys can be altered.
+ */
+ void markPropertiesDirty();
+
+ /**
+ * Marks all properties as dirty, including uninitialized properties.
+ * As a side-effect, primary keys can be altered.
+ */
+ void markAllPropertiesDirty();
+
+ /**
+ * Returns true if the given property of this Storable has never been
+ * loaded or set.
+ *
+ * @param propertyName name of property to interrogate
+ * @throws IllegalArgumentException if property is unknown or is a join
+ */
+ boolean isPropertyUninitialized(String propertyName);
+
+ /**
+ * Returns true if the given property of this Storable has been set, but no
+ * load or store operation has been performed yet.
+ *
+ * @param propertyName name of property to interrogate
+ * @throws IllegalArgumentException if property is unknown or is a join
+ */
+ boolean isPropertyDirty(String propertyName);
+
+ /**
+ * Returns true if the given property of this Storable is clean. All
+ * properties are clean after a successful load or store operation.
+ *
+ * @param propertyName name of property to interrogate
+ * @throws IllegalArgumentException if property is unknown or is a join
+ */
+ boolean isPropertyClean(String propertyName);
+
+ /**
+ * Returns true if the given property exists and is supported. If a
+ * Storable has an {@link Independent} property which is not supported by
+ * the repository, then this method returns false.
+ *
+ * @param propertyName name of property to check
+ */
+ boolean isPropertySupported(String propertyName);
+
+ /**
+ * Returns an exact shallow copy of this object, including the state.
+ */
+ S copy();
+
+ int hashCode();
+
+ /**
+ * True if all properties and fields are equal, but ignoring the state.
+ *
+ * @param obj object to compare to for equality
+ */
+ boolean equals(Object obj);
+
+ /**
+ * True if the supported properties which participate in the primary key
+ * are equal. This is useful to cheaply investigate if two storables refer
+ * to the same entity, regardless of the state of object (specifically the
+ * non-key properties). Unsupported {@link Independent independent}
+ * properties in this or the target are not compared.
+ *
+ * @param obj object to compare to for equality
+ */
+ boolean equalPrimaryKeys(Object obj);
+
+ /**
+ * True if all supported properties for this object are equal. Unsupported
+ * {@link Independent independent} properties in this or the target are not
+ * compared.
+ *
+ * @param obj object to compare to for equality
+ */
+ boolean equalProperties(Object obj);
+
+ /**
+ * Returns a string for debugging purposes that contains all supported
+ * property names and values for this object. Unsupported {@link
+ * Independent independent} properties are not included.
+ */
+ String toString();
+
+ /**
+ * Returns a string for debugging purposes that contains only supported
+ * primary key property names and values for this object. Unsupported
+ * {@link Independent independent} properties are not included.
+ */
+ String toStringKeyOnly();
+}
diff --git a/src/main/java/com/amazon/carbonado/Storage.java b/src/main/java/com/amazon/carbonado/Storage.java
new file mode 100644
index 0000000..3489d63
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Storage.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import com.amazon.carbonado.filter.Filter;
+
+/**
+ * Access for a specific type of {@link Storable} from a {@link Repository}.
+ *
+ * <p>Storage instances are thread-safe and immutable.
+ *
+ * @author Brian S O'Neill
+ */
+public interface Storage<S extends Storable> {
+ /**
+ * Returns the specific type of Storable managed by this object.
+ */
+ Class<S> getStorableType();
+
+ /**
+ * Prepares a new object for loading, inserting, updating, or deleting.
+ *
+ * @return a new data access object
+ */
+ S prepare();
+
+ /**
+ * Query for all Storable instances in this Storage.
+ *
+ * @see #query(String)
+ * @throws FetchException if storage layer throws an exception
+ */
+ Query<S> query() throws FetchException;
+
+ /**
+ * Query for Storable instances against a filter expression. A filter tests
+ * if property values match against specific values specified by '?'
+ * placeholders. The simplest filter compares just one property, like
+ * {@code "ID = ?"}. Filters can also contain several kinds of relational
+ * operators, boolean logic operators, sub-properties, and parentheses. A
+ * more complex example might be {@code "income < ? | (name = ? & address.zipCode != ?)"}.
+ * <p>
+ * When querying for a single Storable instance by its primary key, it is
+ * generally more efficient to call {@link #prepare()}, set primary key
+ * properties, and then call {@link Storable#load()}. For example, consider
+ * an object with a primary key consisting only of the property "ID". It
+ * can be queried as:
+ * <pre>
+ * Storage&lt;UserInfo&gt; users;
+ * UserInfo user = users.query("ID = ?").with(123456).loadOne();
+ * </pre>
+ * The above code will likely open a Cursor in order to verify that just
+ * one object was loaded. Instead, do this:
+ * <pre>
+ * Storage&lt;UserInfo&gt; users;
+ * UserInfo user = users.prepare();
+ * user.setID(123456);
+ * user.load();
+ * </pre>
+ * The complete syntax for query filters follows. Note that:
+ * <ul>
+ * <li> literals are not allowed
+ * <li> logical 'and' operator has precedence over 'or'
+ * <li> logical 'not' operator has precedence over 'and'
+ * <li> '?' placeholders can only appear after relational operators
+ * </ul>
+ * <pre>
+ * Filter = OrFilter
+ * OrFilter = AndFilter { "|" AndFilter }
+ * AndFilter = NotFilter { "&" NotFilter }
+ * NotFilter = [ "!" ] EntityFilter
+ * EntityFilter = PropertyFilter
+ * | "(" Filter ")"
+ * PropertyFilter = ChainedProperty RelOp "?"
+ * RelOp = "=" | "!=" | "&lt;" | "&gt;=" | "&gt;" | "&lt;="
+ * ChainedProperty = Identifier { "." Identifier }
+ * </pre>
+ *
+ * @param filter query filter expression
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalArgumentException if filter is null
+ * @throws MalformedFilterException if expression is malformed
+ * @throws UnsupportedOperationException if given filter is unsupported by repository
+ */
+ Query<S> query(String filter) throws FetchException;
+
+ /**
+ * Query for Storable instances against an explicitly constructed filter
+ * object.
+ *
+ * @param filter query filter
+ * @throws FetchException if storage layer throws an exception
+ * @throws IllegalArgumentException if filter is null
+ * @throws UnsupportedOperationException if given filter is unsupported by repository
+ */
+ Query<S> query(Filter<S> filter) throws FetchException;
+
+ /**
+ * Register a trigger which will be called for overridden methods in the given
+ * trigger implementation. The newly added trigger is invoked before and
+ * after all other triggers. In other words, it is added at the outermost
+ * nesting level.
+ *
+ * @return true if trigger was added, false if trigger was not added
+ * because an equal trigger is already registered
+ * @throws IllegalArgumentException if trigger is null
+ */
+ boolean addTrigger(Trigger<? super S> trigger);
+
+ /**
+ * Remove a trigger which was registered earlier.
+ *
+ * @return true if trigger instance was removed, false if not registered
+ * @throws IllegalArgumentException if trigger is null
+ */
+ boolean removeTrigger(Trigger<? super S> trigger);
+}
diff --git a/src/main/java/com/amazon/carbonado/SupportException.java b/src/main/java/com/amazon/carbonado/SupportException.java
new file mode 100644
index 0000000..cd9a835
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/SupportException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Indicates that an action cannot be supported by the {@link
+ * Repository} it is being requested from. Typically this results from
+ * an attempt to get storage for a {@link Storable} which is not supported,
+ * or an attempt to configure a repository improperly.
+ *
+ * @author Brian S O'Neill
+ */
+public class SupportException extends RepositoryException {
+
+ private static final long serialVersionUID = -6150578915717928592L;
+
+ public SupportException() {
+ super();
+ }
+
+ public SupportException(String message) {
+ super(message);
+ }
+
+ public SupportException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SupportException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/Transaction.java b/src/main/java/com/amazon/carbonado/Transaction.java
new file mode 100644
index 0000000..5c8203f
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Transaction.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Transactions define atomic operations which can be committed or aborted as a
+ * unit. Transactions are entered by calling {@link Repository#enterTransaction()}.
+ * Transactions are thread-local, and so no special action needs to be taken to
+ * bind operations to them. Cursors which are opened in the scope of a
+ * transaction are automatically closed when the transaction is committed or
+ * aborted.
+ *
+ * <p>Transactions do not exit when they are committed. The transaction is
+ * still valid after a commit, but new operations are grouped into a separate
+ * atomic unit. The exit method <em>must</em> be invoked on every
+ * transaction. The following pattern is recommended:
+ *
+ * <pre>
+ * Transaction txn = repository.enterTransaction();
+ * try {
+ * // Make updates to storage layer
+ * ...
+ *
+ * // Commit the changes up to this point
+ * txn.commit();
+ *
+ * // Optionally make more updates
+ * ...
+ *
+ * // Commit remaining changes
+ * txn.commit();
+ * } finally {
+ * // Ensure transaction exits, aborting uncommitted changes if an exception was thrown
+ * txn.exit();
+ * }
+ * </pre>
+ *
+ * <p>Transactions may be nested. Calling commit or abort on an outer
+ * transaction will recursively apply the same operation to all inner
+ * transactions as well. All Cursors contained within are also closed.
+ *
+ * <p>Transaction instances are mutable, but they are thread-safe.
+ *
+ * @author Brian S O'Neill
+ */
+public interface Transaction {
+ /**
+ * If currently in a transaction, commits all changes to the storage layer
+ * since the last commit within the transaction.
+ *
+ * @throws PersistException if storage layer throws an exception
+ */
+ void commit() throws PersistException;
+
+ /**
+ * Closes the current transaction, aborting all changes since the last
+ * commit.
+ *
+ * @throws PersistException if storage layer throws an exception
+ */
+ void exit() throws PersistException;
+
+ /**
+ * Set to true to force all read operations within this transaction to
+ * acquire upgradable or write locks. This option eliminates deadlocks that
+ * may occur when updating records, except it may increase contention.
+ */
+ void setForUpdate(boolean forUpdate);
+
+ /**
+ * Returns true if this transaction is in update mode, which is adjusted by
+ * calling {@link #setForUpdate}.
+ */
+ boolean isForUpdate();
+
+ /**
+ * Specify a desired timeout for aquiring locks within this
+ * transaction. Calling this method may have have no effect at all, if the
+ * repository does not support this feature. In addition, the lock timeout
+ * might not be alterable if the transaction contains uncommitted data.
+ *
+ * <p>Also, the range of lock timeout values supported might be small. For
+ * example, only a timeout value of zero might be supported. In that case,
+ * the transaction is configured to not wait at all when trying to acquire
+ * locks. Expect immediate timeout exceptions when locks cannot be
+ * granted.
+ *
+ * <p>Nested transactions inherit the desired lock timeout of their
+ * parent. Top transactions always begin with the default lock timeout.
+ *
+ * @param timeout Desired lock timeout. If negative, revert lock timeout to
+ * default value.
+ * @param unit Time unit for timeout. If null, revert lock timeout to
+ * default value.
+ */
+ void setDesiredLockTimeout(int timeout, TimeUnit unit);
+
+ /**
+ * Returns the isolation level of this transaction.
+ */
+ IsolationLevel getIsolationLevel();
+}
diff --git a/src/main/java/com/amazon/carbonado/Trigger.java b/src/main/java/com/amazon/carbonado/Trigger.java
new file mode 100644
index 0000000..20b042f
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Trigger.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Callback mechanism to allow custom code to run when a storable is
+ * persisted. By default, the methods defined in this class do
+ * nothing. Subclass and override trigger conditions of interest, and then
+ * {@link Storage#addTrigger register} it. Each overridden trigger method is
+ * called in the same transaction scope as the persist operation.
+ *
+ * <p>To ensure proper nesting, all "before" events are run in the
+ * <em>opposite</em> order that the trigger was registered. All "after" and
+ * "failed" events are run in the same order that the trigger was registered.
+ * In other words, the last added trigger is at the outermost nesting level.
+ *
+ * @author Brian S O'Neill
+ */
+public abstract class Trigger<S> {
+ /**
+ * Called before a storable is to be inserted. The default implementation
+ * does nothing.
+ *
+ * <p>Any exception thrown by this method will cause the insert operation
+ * to rollback and all remaining triggers to not run. The exception is
+ * ultimately passed to the caller of the insert method.
+ *
+ * @param storable storable before being inserted
+ * @return arbitrary state object, passed to afterInsert or failedInsert method
+ */
+ public Object beforeInsert(S storable) throws PersistException {
+ return null;
+ }
+
+ /**
+ * Called before a storable is to be inserted via tryInsert. The default
+ * implementation simply calls {@link #beforeInsert}. Only override if
+ * trigger needs to distinguish between different insert variants.
+ *
+ * <p>Any exception thrown by this method will cause the tryInsert operation
+ * to rollback and all remaining triggers to not run. The exception is
+ * ultimately passed to the caller of the tryInsert method.
+ *
+ * @param storable storable before being inserted
+ * @return arbitrary state object, passed to afterTryInsert or failedInsert method
+ * @see #abortTry
+ */
+ public Object beforeTryInsert(S storable) throws PersistException {
+ return beforeInsert(storable);
+ }
+
+ /**
+ * Called right after a storable has been successfully inserted. The
+ * default implementation does nothing.
+ *
+ * <p>Any exception thrown by this method will cause the insert operation
+ * to rollback and all remaining triggers to not run. The exception is
+ * ultimately passed to the caller of the insert method.
+ *
+ * @param storable storable after being inserted
+ * @param state object returned by beforeInsert method
+ */
+ public void afterInsert(S storable, Object state) throws PersistException {
+ }
+
+ /**
+ * Called right after a storable has been successfully inserted via
+ * tryInsert. The default implementation simply calls {@link #afterInsert}.
+ * Only override if trigger needs to distinguish between different insert
+ * variants.
+ *
+ * <p>Any exception thrown by this method will cause the tryInsert
+ * operation to rollback and all remaining triggers to not run. The
+ * exception is ultimately passed to the caller of the tryInsert method.
+ *
+ * @param storable storable after being inserted
+ * @param state object returned by beforeTryInsert method
+ * @see #abortTry
+ */
+ public void afterTryInsert(S storable, Object state) throws PersistException {
+ afterInsert(storable, state);
+ }
+
+ /**
+ * Called when an insert operation failed due to a unique constraint
+ * violation or an exception was thrown. The main purpose of this method is
+ * to allow any necessary clean-up to occur on the optional state object.
+ *
+ * <p>Any exception thrown by this method will be passed to the current
+ * thread's uncaught exception handler.
+ *
+ * @param storable storable which failed to be inserted
+ * @param state object returned by beforeInsert method, but it may be null
+ */
+ public void failedInsert(S storable, Object state) {
+ }
+
+ /**
+ * Called before a storable is to be updated. The default implementation
+ * does nothing.
+ *
+ * <p>Any exception thrown by this method will cause the update operation
+ * to rollback and all remaining triggers to not run. The exception is
+ * ultimately passed to the caller of the update method.
+ *
+ * @param storable storable before being updated
+ * @return arbitrary state object, passed to afterUpdate or failedUpdate method
+ */
+ public Object beforeUpdate(S storable) throws PersistException {
+ return null;
+ }
+
+ /**
+ * Called before a storable is to be updated via tryUpdate. The default
+ * implementation simply calls {@link #beforeUpdate}. Only override if
+ * trigger needs to distinguish between different update variants.
+ *
+ * <p>Any exception thrown by this method will cause the tryUpdate operation
+ * to rollback and all remaining triggers to not run. The exception is
+ * ultimately passed to the caller of the tryUpdate method.
+ *
+ * @param storable storable before being updated
+ * @return arbitrary state object, passed to afterTryUpdate or failedUpdate method
+ * @see #abortTry
+ */
+ public Object beforeTryUpdate(S storable) throws PersistException {
+ return beforeUpdate(storable);
+ }
+
+ /**
+ * Called right after a storable has been successfully updated. The default
+ * implementation does nothing.
+ *
+ * <p>Any exception thrown by this method will cause the update operation
+ * to rollback and all remaining triggers to not run. The exception is
+ * ultimately passed to the caller of the update method.
+ *
+ * @param storable storable after being updated
+ * @param state optional object returned by beforeUpdate method
+ */
+ public void afterUpdate(S storable, Object state) throws PersistException {
+ }
+
+ /**
+ * Called right after a storable has been successfully updated via
+ * tryUpdate. The default implementation simply calls {@link #afterUpdate}.
+ * Only override if trigger needs to distinguish between different update
+ * variants.
+ *
+ * <p>Any exception thrown by this method will cause the tryUpdate
+ * operation to rollback and all remaining triggers to not run. The
+ * exception is ultimately passed to the caller of the tryUpdate method.
+ *
+ * @param storable storable after being updated
+ * @param state object returned by beforeTryUpdate method
+ * @see #abortTry
+ */
+ public void afterTryUpdate(S storable, Object state) throws PersistException {
+ afterUpdate(storable, state);
+ }
+
+ /**
+ * Called when an update operation failed because the record was missing or
+ * an exception was thrown. The main purpose of this method is to allow any
+ * necessary clean-up to occur on the optional state object.
+ *
+ * <p>Any exception thrown by this method will be passed to the current
+ * thread's uncaught exception handler.
+ *
+ * @param storable storable which failed to be updated
+ * @param state optional object returned by beforeUpdate
+ * method, but it may be null
+ */
+ public void failedUpdate(S storable, Object state) {
+ }
+
+ /**
+ * Called before a storable is to be deleted. The default implementation
+ * does nothing.
+ *
+ * <p>Any exception thrown by this method will cause the delete operation
+ * to rollback and all remaining triggers to not run. The exception is
+ * ultimately passed to the caller of the delete method.
+ *
+ * @param storable storable before being deleted
+ * @return arbitrary state object, passed to afterDelete or failedDelete method
+ */
+ public Object beforeDelete(S storable) throws PersistException {
+ return null;
+ }
+
+ /**
+ * Called before a storable is to be deleted via tryDelete. The default
+ * implementation simply calls {@link #beforeDelete}. Only override if
+ * trigger needs to distinguish between different delete variants.
+ *
+ * <p>Any exception thrown by this method will cause the tryDelete operation
+ * to rollback and all remaining triggers to not run. The exception is
+ * ultimately passed to the caller of the tryDelete method.
+ *
+ * @param storable storable before being deleted
+ * @return arbitrary state object, passed to afterTryDelete or failedDelete method
+ * @see #abortTry
+ */
+ public Object beforeTryDelete(S storable) throws PersistException {
+ return beforeDelete(storable);
+ }
+
+ /**
+ * Called right after a storable has been successfully deleted. The default
+ * implementation does nothing.
+ *
+ * <p>Any exception thrown by this method will cause the delete operation
+ * to rollback and all remaining triggers to not run. The exception is
+ * ultimately passed to the caller of the delete method.
+ *
+ * @param storable storable after being deleted
+ * @param state optional object returned by beforeDelete method
+ */
+ public void afterDelete(S storable, Object state) throws PersistException {
+ }
+
+ /**
+ * Called right after a storable has been successfully deleted via
+ * tryDelete. The default implementation simply calls {@link #afterDelete}.
+ * Only override if trigger needs to distinguish between different delete
+ * variants.
+ *
+ * <p>Any exception thrown by this method will cause the tryDelete
+ * operation to rollback and all remaining triggers to not run. The
+ * exception is ultimately passed to the caller of the tryDelete method.
+ *
+ * @param storable storable after being deleted
+ * @param state object returned by beforeTryDelete method
+ * @see #abortTry
+ */
+ public void afterTryDelete(S storable, Object state) throws PersistException {
+ afterDelete(storable, state);
+ }
+
+ /**
+ * Called when an delete operation failed because the record was missing or
+ * an exception was thrown. The main purpose of this method is to allow any
+ * necessary clean-up to occur on the optional state object.
+ *
+ * <p>Any exception thrown by this method will be passed to the current
+ * thread's uncaught exception handler.
+ *
+ * @param storable storable which failed to be deleted
+ * @param state optional object returned by beforeDelete
+ * method, but it may be null
+ */
+ public void failedDelete(S storable, Object state) {
+ }
+
+ /**
+ * Call to quickly abort a "try" operation, returning false to the
+ * caller. This method should not be called by a non-try trigger method,
+ * since the caller gets thrown an exception with an incomplete stack trace.
+ *
+ * <p>This method never returns normally, but as a convenience, a return
+ * type is defined. The abort exception can be thrown by {@code throw abortTry()},
+ * but the {@code throw} keyword is not needed.
+ */
+ protected Abort abortTry() throws Abort {
+ // Throwing and catching an exception is not terribly expensive, but
+ // creating a new exception is more than an order of magnitude slower.
+ // Therefore, re-use the same instance. It has no stack trace since it
+ // would be meaningless.
+ throw Abort.INSTANCE;
+ }
+
+ public static final class Abort extends PersistException {
+ static final Abort INSTANCE = new Abort();
+
+ private Abort() {
+ super("Trigger aborted operation", null);
+ }
+
+ private Abort(String message) {
+ super(message);
+ super.fillInStackTrace();
+ }
+
+ /**
+ * Override to remove the stack trace.
+ */
+ @Override
+ public Throwable fillInStackTrace() {
+ return null;
+ }
+
+ /**
+ * Returns this exception but with a fresh stack trace. The trace does
+ * not include the original thrower of this exception.
+ */
+ public Abort withStackTrace() {
+ Abort a = new Abort(getMessage());
+
+ StackTraceElement[] trace = a.getStackTrace();
+ if (trace != null && trace.length > 1) {
+ // Trim off this method from the trace, which is element 0.
+ StackTraceElement[] trimmed = new StackTraceElement[trace.length - 1];
+ System.arraycopy(trace, 1, trimmed, 0, trimmed.length);
+ a.setStackTrace(trimmed);
+ }
+
+ return a;
+ }
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/UniqueConstraintException.java b/src/main/java/com/amazon/carbonado/UniqueConstraintException.java
new file mode 100644
index 0000000..a3c89b3
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/UniqueConstraintException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * A UniqueConstraintException is thrown if the {@link Repository} storage
+ * layer has a unique constraint check defined for a property, and a violation
+ * has been detected.
+ *
+ * @author Brian S O'Neill
+ */
+public class UniqueConstraintException extends ConstraintException {
+
+ private static final long serialVersionUID = 8887715791827869110L;
+
+ public UniqueConstraintException() {
+ super();
+ }
+
+ public UniqueConstraintException(String message) {
+ super(message);
+ }
+
+ public UniqueConstraintException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public UniqueConstraintException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/UnsupportedTypeException.java b/src/main/java/com/amazon/carbonado/UnsupportedTypeException.java
new file mode 100644
index 0000000..46535a4
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/UnsupportedTypeException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+/**
+ * Thrown by a dependent {@link Repository} which cannot support an {@link
+ * Independent} {@link Storable}.
+ *
+ * @author Brian S O'Neill
+ */
+public class UnsupportedTypeException extends SupportException {
+ private static final long serialVersionUID = 1L;
+
+ private final Class<? extends Storable> mType;
+
+ public UnsupportedTypeException(Class<? extends Storable> type) {
+ super("Independent type not supported: " + type.getName());
+ mType = type;
+ }
+
+ public Class<? extends Storable> getType() {
+ return mType;
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/Version.java b/src/main/java/com/amazon/carbonado/Version.java
new file mode 100644
index 0000000..9bb326a
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/Version.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package com.amazon.carbonado;
+
+import java.lang.annotation.*;
+
+/**
+ * Designates a {@link Storable} property as being the authoritative version
+ * number for the entire Storable instance. Only one property can have this
+ * designation.
+ *
+ * <p>Support for the version property falls into three categories. A
+ * repository may manage the version; it may respect the version; or it may
+ * merely check the version.
+ *
+ * <p><b>Manage</b>: Each storable with a version property must have one and
+ * only one repository which is responsible for managing the version property.
+ * That repository takes responsibility for establishing the version on insert,
+ * and for auto-incrementing it on update. Under no circumstances should the
+ * version property be incremented manually; this can result in a false
+ * optimistic lock exception, or worse may allow the persistent record to
+ * become corrupted. Prior to incrementing, these repositories will verify
+ * that the version exactly matches the version of the current record, throwing
+ * an {@link OptimisticLockException} otherwise. The JDBC repository is the
+ * canonical example of this sort of repository.
+ *
+ * <p><b>Respect</b>: Repositories which respect the version use the version to
+ * guarantee that updates are idempotent -- that is, that an update is applied
+ * once and only once. These repositories will check that the version property
+ * is strictly greater than the version of the current record, and will
+ * (silently) ignore changes which fail this check.
+ *
+ * <p><b>Check</b>: Philosophically, a version property can be considered part
+ * of the identity of the storable. That is, if the storable has a version
+ * property, it cannot be considered fully specified unless that property is
+ * specified. Thus, the minimal required support for all repositories is to
+ * check that the version is specified on update. All repositories -- even
+ * those which neither check nor manage the version -- will throw an {@link
+ * IllegalStateException} if the version property is not set before update.
+ *
+ * <p>The actual type of the version property can be anything, but some
+ * repositories might only support integers. For maximum portability, version
+ * properties should be a regular 32-bit int.
+ *
+ * <p>Example:<pre>
+ * public interface UserInfo extends Storable {
+ * <b>&#64;Version</b>
+ * int getRecordVersionNumber();
+ *
+ * ...
+ * }
+ * </pre>
+ *
+ * @author Brian S O'Neill
+ * @author Don Schneider
+ * @see OptimisticLockException
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Version {
+}
diff --git a/src/main/java/com/amazon/carbonado/package-info.java b/src/main/java/com/amazon/carbonado/package-info.java
new file mode 100644
index 0000000..62753ed
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+/**
+ * Main user-level interfaces, classes, and annotations for Carbonado.
+ */
+package com.amazon.carbonado;