From 04b06d8fa2ced539126f74f4d8a19c29b62c5730 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Wed, 30 Aug 2006 02:05:21 +0000 Subject: Add layout evolution support --- .../java/com/amazon/carbonado/layout/Layout.java | 371 +++++++++++++++++++++ .../amazon/carbonado/layout/LayoutCapability.java | 51 +++ .../com/amazon/carbonado/layout/LayoutFactory.java | 243 ++++++++++++++ .../amazon/carbonado/layout/LayoutProperty.java | 191 +++++++++++ .../com/amazon/carbonado/layout/StoredLayout.java | 107 ++++++ .../carbonado/layout/StoredLayoutProperty.java | 126 +++++++ .../com/amazon/carbonado/layout/Unevolvable.java | 27 ++ .../com/amazon/carbonado/layout/package-info.java | 40 +++ 8 files changed, 1156 insertions(+) create mode 100644 src/main/java/com/amazon/carbonado/layout/Layout.java create mode 100644 src/main/java/com/amazon/carbonado/layout/LayoutCapability.java create mode 100644 src/main/java/com/amazon/carbonado/layout/LayoutFactory.java create mode 100644 src/main/java/com/amazon/carbonado/layout/LayoutProperty.java create mode 100644 src/main/java/com/amazon/carbonado/layout/StoredLayout.java create mode 100644 src/main/java/com/amazon/carbonado/layout/StoredLayoutProperty.java create mode 100644 src/main/java/com/amazon/carbonado/layout/Unevolvable.java create mode 100644 src/main/java/com/amazon/carbonado/layout/package-info.java (limited to 'src/main/java/com/amazon/carbonado/layout') diff --git a/src/main/java/com/amazon/carbonado/layout/Layout.java b/src/main/java/com/amazon/carbonado/layout/Layout.java new file mode 100644 index 0000000..b751fb1 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/layout/Layout.java @@ -0,0 +1,371 @@ +/* + * 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.layout; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.joda.time.DateTime; + +import org.cojen.util.SoftValuedHashMap; + +import com.amazon.carbonado.Cursor; +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.FetchNoneException; +import com.amazon.carbonado.PersistException; +import com.amazon.carbonado.Query; +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.SupportException; +import com.amazon.carbonado.info.StorableInfo; +import com.amazon.carbonado.info.StorableProperty; +import com.amazon.carbonado.synthetic.SyntheticKey; +import com.amazon.carbonado.synthetic.SyntheticProperty; +import com.amazon.carbonado.synthetic.SyntheticStorableBuilder; +import com.amazon.carbonado.util.AnnotationDescPrinter; + +/** + * Describes the layout of a specific generation of a storable. + * + * @author Brian S O'Neill + * @see LayoutFactory + */ +public class Layout { + private static Map> cReconstructed; + + static { + cReconstructed = Collections.synchronizedMap(new SoftValuedHashMap()); + } + + static Class reconstruct(final Layout layout, ClassLoader loader) + throws FetchException, SupportException + { + synchronized (cReconstructed) { + Long key = layout.getLayoutID(); + Class clazz = cReconstructed.get(key); + if (clazz != null) { + return clazz; + } + + SyntheticStorableBuilder builder = + new SyntheticStorableBuilder(layout.getStorableTypeName(), loader); + + // Make sure reconstructed class encodes the same as before. + builder.setEvolvable(true); + + builder.setClassNameProvider(new SyntheticStorableBuilder.ClassNameProvider() { + public String getName() { + return layout.getStorableTypeName(); + } + + // The name of the auto-generated class should not be made with + // createExplicit. Otherwise, property type changes will + // conflict, since the reconstructed class name is the same. + public boolean isExplicit() { + return false; + } + }); + + SyntheticKey primaryKey = builder.addPrimaryKey(); + + for (LayoutProperty property : layout.getAllProperties()) { + Class propClass = property.getPropertyType(loader); + if (Query.class.isAssignableFrom(propClass) || + Storable.class.isAssignableFrom(propClass)) { + // Accidentally stored join property in layout, caused by a + // bug in a previous version of Layout. Move on. + continue; + } + + SyntheticProperty synthProp = + builder.addProperty(property.getPropertyName(), propClass); + + synthProp.setIsNullable(property.isNullable()); + synthProp.setIsVersion(property.isVersion()); + + if (property.isPrimaryKeyMember()) { + primaryKey.addProperty(property.getPropertyName()); + } + + if (property.getAdapterTypeName() != null) { + String desc = property.getAdapterParams(); + if (desc == null) { + desc = AnnotationDescPrinter + .makePlainDescriptor(property.getAdapterTypeName()); + } + synthProp.addAccessorAnnotationDescriptor(desc); + } + } + + clazz = builder.build(); + + cReconstructed.put(key, clazz); + return clazz; + } + } + + private final LayoutFactory mLayoutFactory; + private final StoredLayout mStoredLayout; + + private transient List mAllProperties; + + /** + * Creates a Layout around an existing storable. + */ + Layout(LayoutFactory factory, StoredLayout storedLayout) { + mLayoutFactory = factory; + mStoredLayout = storedLayout; + } + + /** + * Copies layout information into freshly prepared storables. Call insert + * (on this class) to persist them. + */ + Layout(LayoutFactory factory, StorableInfo info, long layoutID) { + mLayoutFactory = factory; + + StoredLayout storedLayout = factory.mLayoutStorage.prepare(); + mStoredLayout = storedLayout; + + storedLayout.setLayoutID(layoutID); + storedLayout.setStorableTypeName(info.getStorableType().getName()); + storedLayout.setCreationTimestamp(System.currentTimeMillis()); + try { + storedLayout.setCreationUser(System.getProperty("user.name")); + } catch (SecurityException e) { + // Can't get user, no big deal. + } + try { + storedLayout.setCreationHost(InetAddress.getLocalHost().getHostName()); + } catch (UnknownHostException e) { + // Can't get host, no big deal. + } catch (SecurityException e) { + // Can't get host, no big deal. + } + + Collection> properties = info.getAllProperties().values(); + List list = new ArrayList(properties.size()); + int ordinal = 0; + for (StorableProperty property : properties) { + if (property.isJoin()) { + continue; + } + StoredLayoutProperty storedLayoutProperty = mLayoutFactory.mPropertyStorage.prepare(); + list.add(new LayoutProperty(storedLayoutProperty, property, layoutID, ordinal)); + ordinal++; + } + + mAllProperties = Collections.unmodifiableList(list); + } + + /** + * Returns a unique identifier for this layout. + */ + public long getLayoutID() { + return mStoredLayout.getLayoutID(); + } + + /** + * Storable type name is a fully qualified Java class name. + */ + public String getStorableTypeName() { + return mStoredLayout.getStorableTypeName(); + } + + /** + * Returns the generation of this layout, where zero represents the first + * generation. + */ + public int getGeneration() { + return mStoredLayout.getGeneration(); + } + + /** + * Returns all the non-primary key properties of this layout, in their + * proper order. + */ + public List getDataProperties() throws FetchException { + List all = getAllProperties(); + List data = new ArrayList(all.size() - 1); + for (LayoutProperty property : all) { + if (!property.isPrimaryKeyMember()) { + data.add(property); + } + } + return Collections.unmodifiableList(data); + } + + /** + * Returns all the properties of this layout, in their proper order. + */ + public List getAllProperties() throws FetchException { + if (mAllProperties == null) { + Cursor cursor = mStoredLayout.getProperties() + .orderBy("ordinal") + .fetch(); + + List list = new ArrayList(); + + while (cursor.hasNext()) { + list.add(new LayoutProperty(cursor.next())); + } + + mAllProperties = Collections.unmodifiableList(list); + } + + return mAllProperties; + } + + /** + * Returns the date and time for when this layout generation was created. + */ + public DateTime getCreationDateTime() { + return new DateTime(mStoredLayout.getCreationTimestamp()); + } + + /** + * Returns the user that created this layout generation. + */ + public String getCreationUser() { + return mStoredLayout.getCreationUser(); + } + + /** + * Returns the host machine that created this generation. + */ + public String getCreationHost() { + return mStoredLayout.getCreationHost(); + } + + /** + * Returns the layout for a particular generation of this layout's type. + * + * @throws FetchNoneException if generation not found + */ + public Layout getGeneration(int generation) throws FetchNoneException, FetchException { + StoredLayout storedLayout = mLayoutFactory.mLayoutStorage + .query("storableTypeName = ? & generation = ?") + .with(getStorableTypeName()).with(generation) + .loadOne(); + return new Layout(mLayoutFactory, storedLayout); + } + + /** + * Returns the previous known generation of the storable's layout, or null + * if none. + * + * @return a layout with a lower generation, or null if none + */ + public Layout previousGeneration() throws FetchException { + Cursor cursor = mLayoutFactory.mLayoutStorage + .query("storableTypeName = ? & generation < ?") + .with(getStorableTypeName()).with(getGeneration()) + .orderBy("-generation") + .fetch(); + + try { + if (cursor.hasNext()) { + return new Layout(mLayoutFactory, cursor.next()); + } + } finally { + cursor.close(); + } + + return null; + } + + /** + * Returns the next known generation of the storable's layout, or null + * if none. + * + * @return a layout with a higher generation, or null if none + */ + public Layout nextGeneration() throws FetchException { + Cursor cursor = + mLayoutFactory.mLayoutStorage.query("storableTypeName = ? & generation > ?") + .with(getStorableTypeName()).with(getGeneration()) + .orderBy("+generation") + .fetch(); + + try { + if (cursor.hasNext()) { + return new Layout(mLayoutFactory, cursor.next()); + } + } finally { + cursor.close(); + } + + return null; + } + + /** + * Reconstructs the storable type defined by this layout by returning an + * auto-generated class. The reconstructed storable type will not contain + * everything in the original, but rather the minimum required to decode + * persisted instances. + */ + public Class reconstruct() throws FetchException, SupportException { + return reconstruct(null); + } + + /** + * Reconstructs the storable type defined by this layout by returning an + * auto-generated class. The reconstructed storable type will not contain + * everything in the original, but rather the minimum required to decode + * persisted instances. + * + * @param loader optional ClassLoader to load reconstruct class into, if it + * has not been loaded yet + */ + public Class reconstruct(ClassLoader loader) + throws FetchException, SupportException + { + Class reconstructed = reconstruct(this, loader); + mLayoutFactory.registerReconstructed(reconstructed, this); + return reconstructed; + } + + /** + * Returns true if the given layout matches this one. Layout ID, + * generation, and creation info is not considered in the comparison. + */ + public boolean equalLayouts(Layout layout) throws FetchException { + if (this == layout) { + return true; + } + return getStorableTypeName().equals(layout.getStorableTypeName()) + && getAllProperties().equals(layout.getAllProperties()); + } + + // Assumes caller is in a transaction. + void insert(int generation) throws PersistException { + if (mAllProperties == null) { + throw new IllegalStateException(); + } + mStoredLayout.setGeneration(generation); + mStoredLayout.insert(); + for (LayoutProperty property : mAllProperties) { + property.insert(); + } + } +} diff --git a/src/main/java/com/amazon/carbonado/layout/LayoutCapability.java b/src/main/java/com/amazon/carbonado/layout/LayoutCapability.java new file mode 100644 index 0000000..e43fc23 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/layout/LayoutCapability.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.layout; + +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.FetchNoneException; +import com.amazon.carbonado.PersistException; +import com.amazon.carbonado.Storable; + +import com.amazon.carbonado.capability.Capability; + +/** + * Capability to get layout information on any storable generation. + * + * @author Brian S O'Neill + */ +public interface LayoutCapability extends Capability { + /** + * Returns the layout matching the current definition of the given type. + * + * @throws PersistException if type represents a new generation, but + * persisting this information failed + */ + public Layout layoutFor(Class type) + throws FetchException, PersistException; + + /** + * Returns the layout for a particular generation of the given type. + * + * @param generation desired generation + * @throws FetchNoneException if generation not found + */ + public Layout layoutFor(Class type, int generation) + throws FetchException, FetchNoneException; +} diff --git a/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java b/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java new file mode 100644 index 0000000..3557db0 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java @@ -0,0 +1,243 @@ +/* + * 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.layout; + +import java.lang.annotation.Annotation; +import java.util.Map; + +import org.cojen.util.SoftValuedHashMap; + +import com.amazon.carbonado.Cursor; +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.FetchNoneException; +import com.amazon.carbonado.PersistException; +import com.amazon.carbonado.Repository; +import com.amazon.carbonado.RepositoryException; +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.Storage; +import com.amazon.carbonado.Transaction; + +import com.amazon.carbonado.info.StorableInfo; +import com.amazon.carbonado.info.StorableIntrospector; +import com.amazon.carbonado.info.StorableProperty; +import com.amazon.carbonado.info.StorablePropertyAdapter; +import com.amazon.carbonado.info.StorablePropertyAnnotation; + +/** + * Factory for obtaining references to storable layouts. + * + * @author Brian S O'Neill + */ +public class LayoutFactory implements LayoutCapability { + // The first entry is the primary hash multiplier. Subsequent ones are + // rehash multipliers. + private static final int[] HASH_MULTIPLIERS = {31, 63}; + + final Repository mRepository; + final Storage mLayoutStorage; + final Storage mPropertyStorage; + + private Map, Layout> mReconstructed; + + /** + * @throws com.amazon.carbonado.SupportException if underlying repository + * does not support the storables for persisting storable layouts + */ + public LayoutFactory(Repository repo) throws RepositoryException { + mRepository = repo; + mLayoutStorage = repo.storageFor(StoredLayout.class); + mPropertyStorage = repo.storageFor(StoredLayoutProperty.class); + } + + /** + * Returns the layout matching the current definition of the given type. + * + * @throws PersistException if type represents a new generation, but + * persisting this information failed + */ + public Layout layoutFor(Class type) + throws FetchException, PersistException + { + synchronized (this) { + if (mReconstructed != null) { + Layout layout = mReconstructed.get(type); + if (layout != null) { + return layout; + } + } + } + + StorableInfo info = StorableIntrospector.examine(type); + + Transaction txn = mRepository.enterTransaction(); + try { + // If type represents a new generation, then a new layout needs to + // be inserted. + Layout newLayout = null; + + for (int i=0; i= HASH_MULTIPLIERS.length - 1) { + // No more rehashes to attempt. This should be extremely, + // extremely rare, unless there is a bug somewhere. + throw new FetchException("Unable to generate unique layout identifier"); + } + } + + // If this point is reached, then type represents a new + // generation. Calculate next generation value and insert. + + assert(newLayout != null); + int generation = 0; + + Cursor cursor = mLayoutStorage + .query("storableTypeName = ?") + .with(info.getStorableType().getName()) + .orderBy("-generation") + .fetch(); + + try { + if (cursor.hasNext()) { + generation = cursor.next().getGeneration() + 1; + } + } finally { + cursor.close(); + } + + newLayout.insert(generation); + txn.commit(); + + return newLayout; + } finally { + txn.exit(); + } + } + + /** + * Returns the layout for a particular generation of the given type. + * + * @param generation desired generation + * @throws FetchNoneException if generation not found + */ + public Layout layoutFor(Class type, int generation) + throws FetchException, FetchNoneException + { + StoredLayout storedLayout = + mLayoutStorage.query("storableTypeName = ? & generation = ?") + .with(type.getName()).with(generation) + .loadOne(); + return new Layout(this, storedLayout); + } + + synchronized void registerReconstructed + (Class reconstructed, Layout layout) + { + if (mReconstructed == null) { + mReconstructed = new SoftValuedHashMap(); + } + mReconstructed.put(reconstructed, layout); + } + + /** + * Creates a long hash code that attempts to mix in all relevent layout + * elements. + */ + private long mixInHash(long hash, StorableInfo info, int multiplier) { + hash = mixInHash(hash, info.getStorableType().getName(), multiplier); + + for (StorableProperty property : info.getAllProperties().values()) { + if (!property.isJoin()) { + hash = mixInHash(hash, property, multiplier); + } + } + + return hash; + } + + /** + * Creates a long hash code that attempts to mix in all relevent layout + * elements. + */ + private long mixInHash(long hash, StorableProperty property, int multiplier) { + hash = mixInHash(hash, property.getName(), multiplier); + hash = mixInHash(hash, property.getType().getName(), multiplier); + hash = hash * multiplier + (property.isNullable() ? 1 : 2); + hash = hash * multiplier + (property.isPrimaryKeyMember() ? 1 : 2); + + // Keep this in for compatibility with prior versions of hash code. + hash = hash * multiplier + 1; + + if (property.getAdapter() != null) { + // Keep this in for compatibility with prior versions of hash code. + hash += 1; + + StorablePropertyAdapter adapter = property.getAdapter(); + StorablePropertyAnnotation annotation = adapter.getAnnotation(); + + hash = mixInHash(hash, annotation.getAnnotationType().getName(), multiplier); + + // Annotation may contain parameters which affect how property + // value is stored. So mix that in too. + Annotation ann = annotation.getAnnotation(); + if (ann != null) { + // Okay to mix in annotation hash code since Annotation + // documentation defines the implementation. It should remain + // stable between releases, but it is not critical that the + // hash code always comes out the same. The result would be a + // duplicate stored layout, but with a different generation. + // Stored entries will be converted from the "different" + // generation, causing a very slight performance degradation. + hash = hash * multiplier + ann.hashCode(); + } + } + + return hash; + } + + private long mixInHash(long hash, CharSequence value, int multiplier) { + for (int i=value.length(); --i>=0; ) { + hash = hash * multiplier + value.charAt(i); + } + return hash; + } +} diff --git a/src/main/java/com/amazon/carbonado/layout/LayoutProperty.java b/src/main/java/com/amazon/carbonado/layout/LayoutProperty.java new file mode 100644 index 0000000..4a7dea1 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/layout/LayoutProperty.java @@ -0,0 +1,191 @@ +/* + * 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.layout; + +import org.cojen.classfile.TypeDesc; + +import com.amazon.carbonado.PersistException; +import com.amazon.carbonado.SupportException; + +import com.amazon.carbonado.info.StorableProperty; +import com.amazon.carbonado.info.StorablePropertyAdapter; +import com.amazon.carbonado.info.StorablePropertyAnnotation; + +import com.amazon.carbonado.util.AnnotationDescPrinter; + +/** + * Describes how a property is defined in a specific generation of a storable. + * + * @author Brian S O'Neill + * @see Layout + */ +public class LayoutProperty { + private final StoredLayoutProperty mStoredLayoutProperty; + + /** + * Creates a LayoutProperty around an existing storable. + */ + LayoutProperty(StoredLayoutProperty storedLayoutProperty) { + mStoredLayoutProperty = storedLayoutProperty; + } + + /** + * Copies properties into a freshly prepared storable. Call insert (on this + * class) to persist it. + * + * @param storedLayoutProperty freshly prepared storable + * @param property source of data to copy into storable + */ + LayoutProperty(StoredLayoutProperty storedLayoutProperty, + StorableProperty property, + long layoutID, + int ordinal) + { + mStoredLayoutProperty = storedLayoutProperty; + + storedLayoutProperty.setLayoutID(layoutID); + storedLayoutProperty.setOrdinal(ordinal); + storedLayoutProperty.setPropertyName(property.getName()); + storedLayoutProperty.setPropertyTypeDescriptor + (TypeDesc.forClass(property.getType()).getDescriptor()); + storedLayoutProperty.setNullable(property.isNullable()); + storedLayoutProperty.setVersion(property.isVersion()); + storedLayoutProperty.setPrimaryKeyMember(property.isPrimaryKeyMember()); + + if (property.getAdapter() != null) { + StorablePropertyAdapter adapter = property.getAdapter(); + StorablePropertyAnnotation spa = adapter.getAnnotation(); + if (spa == null || spa.getAnnotation() == null) { + storedLayoutProperty.setAdapterTypeName(null); + storedLayoutProperty.setAdapterParams(null); + } else { + storedLayoutProperty.setAdapterTypeName(spa.getAnnotationType().getName()); + + StringBuilder b = new StringBuilder(); + AnnotationDescPrinter printer = new AnnotationDescPrinter(true, b); + printer.visit(spa.getAnnotation()); + + storedLayoutProperty.setAdapterParams(b.toString()); + } + } + } + + public String getPropertyName() { + return mStoredLayoutProperty.getPropertyName(); + } + + /** + * Property type descriptor is a Java type descriptor. + */ + public String getPropertyTypeDescriptor() { + return mStoredLayoutProperty.getPropertyTypeDescriptor(); + } + + public Class getPropertyType() throws SupportException { + return getPropertyType(null); + } + + public Class getPropertyType(ClassLoader loader) throws SupportException { + TypeDesc type = TypeDesc.forDescriptor(getPropertyTypeDescriptor()); + Class propClass = type.toClass(loader); + if (propClass == null) { + throw new SupportException + ("Unable to find class \"" + type.getRootName() + "\" for property \"" + + getPropertyName() + '"'); + } + return propClass; + } + + /** + * Returns true of property can be set to null. + */ + public boolean isNullable() { + return mStoredLayoutProperty.isNullable(); + } + + /** + * Returns true if property is a member of the primary key. + */ + public boolean isPrimaryKeyMember() { + return mStoredLayoutProperty.isPrimaryKeyMember(); + } + + /** + * Returns true if this property is the designated version number for the + * Storable. + */ + public boolean isVersion() { + return mStoredLayoutProperty.isVersion(); + } + + /** + * Adapter type name is a fully qualified Java class name. If property has + * no adapter, then null is returned. + */ + public String getAdapterTypeName() { + return mStoredLayoutProperty.getAdapterTypeName(); + } + + /** + * Parameters for adapter, or null if property has no explicit adapter. + */ + public String getAdapterParams() { + return mStoredLayoutProperty.getAdapterParams(); + } + + @Override + public int hashCode() { + return mStoredLayoutProperty.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof LayoutProperty) { + StoredLayoutProperty thisStoredLayoutProperty = mStoredLayoutProperty; + StoredLayoutProperty other = ((LayoutProperty) obj).mStoredLayoutProperty; + + boolean result = thisStoredLayoutProperty.equalProperties(other); + if (result) { + return result; + } + + // Version might be only difference, which is fine. + if (thisStoredLayoutProperty.getVersionNumber() != other.getVersionNumber()) { + thisStoredLayoutProperty = thisStoredLayoutProperty.copy(); + thisStoredLayoutProperty.setVersionNumber(other.getVersionNumber()); + return thisStoredLayoutProperty.equalProperties(other); + } + } + + return false; + } + + @Override + public String toString() { + return mStoredLayoutProperty.toString(); + } + + void insert() throws PersistException { + mStoredLayoutProperty.insert(); + } +} diff --git a/src/main/java/com/amazon/carbonado/layout/StoredLayout.java b/src/main/java/com/amazon/carbonado/layout/StoredLayout.java new file mode 100644 index 0000000..b7065d9 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/layout/StoredLayout.java @@ -0,0 +1,107 @@ +/* + * 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.layout; + +import com.amazon.carbonado.AlternateKeys; +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.Join; +import com.amazon.carbonado.Key; +import com.amazon.carbonado.Nullable; +import com.amazon.carbonado.PrimaryKey; +import com.amazon.carbonado.Query; +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.Version; + +/** + * Stored information regarding the layout of a Storable type, which is used + * internally by {@link Layout}. This interface is public only because + * Carbonado requires storable type definitions to be public. + * + * @author Brian S O'Neill + */ +@AlternateKeys({ + @Key({"storableTypeName", "generation"}) +}) +@PrimaryKey("layoutID") +public interface StoredLayout extends Storable, Unevolvable { + long getLayoutID(); + + void setLayoutID(long typeID); + + /** + * Storable type name is a fully qualified Java class name. + */ + String getStorableTypeName(); + + void setStorableTypeName(String typeName); + + /** + * Generation of storable, where 0 represents the first generation. + */ + int getGeneration(); + + void setGeneration(int generation); + + /** + * Returns the milliseconds from 1970-01-01T00:00:00Z when this record was + * created. + */ + long getCreationTimestamp(); + + void setCreationTimestamp(long timestamp); + + /** + * Returns the user that created this generation. + */ + @Nullable + String getCreationUser(); + + void setCreationUser(String user); + + /** + * Returns the host machine that created this generation. + */ + @Nullable + String getCreationHost(); + + void setCreationHost(String host); + + @Join + Query getProperties() throws FetchException; + + /** + * Record version number for this StoredTypeLayout instance. Some encoding + * strategies require a version number. + */ + @Version + int getVersionNumber(); + + void setVersionNumber(int version); + + /** + * Since this record cannot evolve, this property allows it to be extended + * without conflicting with existing records. This record cannot evolve + * because an evolution strategy likely depends on this interface remaining + * stable, avoiding a cyclic dependency. + */ + @Nullable + byte[] getExtraData(); + + void setExtraData(byte[] data); +} diff --git a/src/main/java/com/amazon/carbonado/layout/StoredLayoutProperty.java b/src/main/java/com/amazon/carbonado/layout/StoredLayoutProperty.java new file mode 100644 index 0000000..96a990a --- /dev/null +++ b/src/main/java/com/amazon/carbonado/layout/StoredLayoutProperty.java @@ -0,0 +1,126 @@ +/* + * 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.layout; + +import com.amazon.carbonado.AlternateKeys; +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.Join; +import com.amazon.carbonado.Key; +import com.amazon.carbonado.Nullable; +import com.amazon.carbonado.PrimaryKey; +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.Version; + +/** + * Stored property joined to a {@link StoredLayout}, which is used internally + * by {@link LayoutProperty}. This interface is public only because Carbonado + * requires storable type definitions to be public. + * + * @author Brian S O'Neill + */ +@AlternateKeys({ + @Key({"layoutID", "propertyName"}) +}) +@PrimaryKey({"layoutID", "ordinal"}) +public interface StoredLayoutProperty extends Storable, Unevolvable { + long getLayoutID(); + + void setLayoutID(long typeID); + + /** + * Ordinal defines the order in which this property appears in it enclosing + * layout. + */ + int getOrdinal(); + + void setOrdinal(int ordinal); + + String getPropertyName(); + + void setPropertyName(String name); + + /** + * Property type descriptor is a Java type descriptor. + */ + String getPropertyTypeDescriptor(); + + void setPropertyTypeDescriptor(String type); + + /** + * Returns true of property value can be set to null. + */ + boolean isNullable(); + + void setNullable(boolean nullable); + + /** + * Returns true if property is a member of the primary key. + */ + boolean isPrimaryKeyMember(); + + void setPrimaryKeyMember(boolean pk); + + /** + * Returns true if this property is the designated version number for the + * Storable. + */ + boolean isVersion(); + + void setVersion(boolean version); + + /** + * Adapter type name is a fully qualified Java class name. If property has + * no adapter, then null is returned. + */ + @Nullable + String getAdapterTypeName(); + + void setAdapterTypeName(String name); + + /** + * Parameters for adapter, or null if property has no explicit adapter. + */ + @Nullable + String getAdapterParams(); + + void setAdapterParams(String params); + + @Join + StoredLayout getEnclosingLayout() throws FetchException; + + /** + * Record version number for this StoredPropertyLayout instance. Some + * encoding strategies require a version number. + */ + @Version + int getVersionNumber(); + + void setVersionNumber(int version); + + /** + * Since this record cannot evolve, this property allows it to be extended + * without conflicting with existing records. This record cannot evolve + * because an evolution strategy likely depends on this interface remaining + * stable, avoiding a cyclic dependency. + */ + @Nullable + byte[] getExtraData(); + + void setExtraData(byte[] data); +} diff --git a/src/main/java/com/amazon/carbonado/layout/Unevolvable.java b/src/main/java/com/amazon/carbonado/layout/Unevolvable.java new file mode 100644 index 0000000..5b10d5f --- /dev/null +++ b/src/main/java/com/amazon/carbonado/layout/Unevolvable.java @@ -0,0 +1,27 @@ +/* + * 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.layout; + +/** + * Marker interface for storables that are not allowed to evolve. + * + * @author Brian S O'Neill + */ +public interface Unevolvable { +} diff --git a/src/main/java/com/amazon/carbonado/layout/package-info.java b/src/main/java/com/amazon/carbonado/layout/package-info.java new file mode 100644 index 0000000..a3cbcf7 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/layout/package-info.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. + */ + +/** + * Support for recording the evolution of a storable's layout, used internally + * by some repositories. This allows storable's to evolve. Enough information + * is recorded in the {@link com.amazon.carbonado.layout.Layout layout} such + * that an older generation can be reconstructed, allowing it to be decoded + * from persistent storage. + * + *

A storable generation is different than a storable {@link + * com.amazon.carbonado.Version version}. The version increases with each + * update of an instance, whereas the generation increases when the + * storable type definition changes. The version number is stored with each + * instance, and the generation is stored via the classes in this package. + * + *

Whenever a property is added or removed from a storable, the storable + * layout is assigned a new generation value. If the storable layout reverts to + * a previous generation's layout, no new generation value is created. Instead, + * the generation value of the current storable will match the previous + * generation. + * + * @see com.amazon.carbonado.layout.LayoutFactory + */ +package com.amazon.carbonado.layout; -- cgit v1.2.3