/** * Create a new instance of the given model class in this transaction. * * @param type Java object model type * @param <T> Java model type * @return newly created instance * @throws IllegalArgumentException if {@code type} is not a known Java object model type * @throws StaleTransactionException if this transaction is no longer usable */ public <T> T create(Class<T> type) { return this.create(this.jdb.getJClass(type)); }
/** * Create a new, empty {@link SnapshotJTransaction} backed by a {@link NavigableMapKVStore}. * * <p> * The returned {@link SnapshotJTransaction} does not support {@link SnapshotJTransaction#commit commit()} or * {@link SnapshotJTransaction#rollback rollback()}, and can be used indefinitely. * * @param validationMode the {@link ValidationMode} to use for the snapshot transaction * @return initially empty snapshot transaction */ public SnapshotJTransaction createSnapshotTransaction(ValidationMode validationMode) { return this.createSnapshotTransaction(new NavigableMapKVStore(), true, validationMode); }
/** * Create a new transaction using an already-opened {@link KVTransaction}. * * <p> * This does not invoke {@link JTransaction#setCurrent JTransaction.setCurrent()}: the caller is responsible * for doing that if necessary. However, this method does arrange for * {@link JTransaction#setCurrent JTransaction.setCurrent}{@code (null)} to be invoked as soon as the * returned transaction is committed (or rolled back), assuming {@link JTransaction#getCurrent} returns the * {@link JTransaction} returned here at that time. * * @param kvt already opened key/value store transaction * @param allowNewSchema whether creating a new schema version is allowed * @param validationMode the {@link ValidationMode} to use for the new transaction * @return the newly created transaction * @throws io.permazen.core.InvalidSchemaException if the schema does not match what's recorded in the * database for the schema version provided to the constructor * @throws io.permazen.core.InvalidSchemaException if the schema version provided to the constructor * is not recorded in the database and {@code allowNewSchema} is false * @throws io.permazen.core.InvalidSchemaException if the schema version provided to the constructor * is not recorded in the database and {@code allowNewSchema} is true, but the schema is incompatible * with one or more previous schemas alread recorded in the database (i.e., the same storage ID is used * incompatibly between schema versions) * @throws io.permazen.core.InconsistentDatabaseException if inconsistent or invalid meta-data is detected in the database * @throws IllegalArgumentException if {@code kvt} or {@code validationMode} is null */ public JTransaction createTransaction(KVTransaction kvt, boolean allowNewSchema, ValidationMode validationMode) { return this.createTransaction( this.db.createTransaction(kvt, this.getSchemaModel(), this.configuredVersion, allowNewSchema), validationMode); }
private static CompositeIndexInfo findCompositeIndex(Permazen jdb, Class<?> startType, String indexName, int numValues) { CompositeIndexInfo indexInfo = null; for (JClass<?> jclass : jdb.getJClasses(startType)) { final JCompositeIndex index = jclass.jcompositeIndexesByName.get(indexName); if (index != null) { final CompositeIndexInfo candidate = jdb.getIndexInfo(index.storageId, CompositeIndexInfo.class); if (indexInfo != null && !candidate.equals(indexInfo)) { throw new IllegalArgumentException("ambiguous composite index name `" + indexName + "': multiple incompatible composite indexes with that name exist on sub-types of " + startType.getName()); } indexInfo = candidate; } } if (indexInfo == null) { throw new IllegalArgumentException("no composite index named `" + indexName + "' exists on any sub-type of " + startType.getName()); } if (numValues != indexInfo.getFieldTypes().size()) { throw new IllegalArgumentException("composite index `" + indexName + "' on " + startType.getName() + " has " + indexInfo.getFieldTypes().size() + " fields, not " + numValues); } return indexInfo; }
final SchemaObjectType schemaType = ReferencePath.this.jdb.getNameIndex().getSchemaObjectType(typeName); if (schemaType != null) type = ReferencePath.this.jdb.getJClass(schemaType.getStorageId()).getType(); else { try { List<? extends JClass<?>> jclasses = ReferencePath.this.jdb.getJClasses(type); if (type.isAssignableFrom(UntypedJObject.class)) { final ArrayList<JClass<?>> jclasses2 = new ArrayList<>(jclasses.size() + 1);
storageId = this.getStorageIdGenerator(annotation, type).generateClassStorageId(type, name); jclass = this.createJClass(name, storageId, type); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("invalid @" + PermazenType.class.getSimpleName() this.addJClass(jclass); this.log.debug("added Java model class `" + jclass.name + "' with storage ID " + jclass.storageId); final JSimpleField simpleField = (JSimpleField)jfield; if (simpleField.indexed) this.addIndexInfo(simpleField, descriptionMap); } else if (jfield instanceof JComplexField) { final JComplexField parentField = (JComplexField)jfield; for (JSimpleField subField : parentField.getSubFields()) { if (subField.indexed) this.addIndexInfo(subField, descriptionMap); this.addIndexInfo(index, descriptionMap); this.typeFieldMap.put(this.getTypeFieldKey(jclass.storageId, jfield.storageId), jfield); if (jfield instanceof JComplexField) { final JComplexField parentField = (JComplexField)jfield; for (JSimpleField subField : parentField.getSubFields()) this.typeFieldMap.put(this.getTypeFieldKey(jclass.storageId, subField.storageId), subField); this.db.validateSchema(this.getSchemaModel());
/** * Get the {@code byte[]} key in the underlying key/value store corresponding to the specified field in the specified object. * * <p> * Notes: * <ul> * <li>Complex fields utilize mutiple keys; the return value is the common prefix of all such keys.</li> * <li>The {@link io.permazen.kv.KVDatabase} should not be modified directly, otherwise behavior is undefined</li> * </ul> * * @param jobj Java model object * @param fieldName the name of a field in {@code jobj}'s type * @return the {@link io.permazen.kv.KVDatabase} key of the field in the specified object * @throws TypeNotInSchemaVersionException if the current schema version does not contain the object's type * @throws IllegalArgumentException if {@code jobj} does not contain the specified field * @throws IllegalArgumentException if {@code fieldName} is otherwise invalid * @throws IllegalArgumentException if either parameter is null * @see io.permazen.kv.KVTransaction#watchKey KVTransaction.watchKey() * @see io.permazen.core.Transaction#getKey(ObjId, int) Transaction.getKey() */ public byte[] getKey(JObject jobj, String fieldName) { Preconditions.checkArgument(jobj != null, "null jobj"); final Class<?> type = this.jdb.getJClass(jobj.getObjId()).type; final ReferencePath refPath = this.jdb.parseReferencePath(type, fieldName, true, false); if (refPath.getReferenceFields().length > 0) throw new IllegalArgumentException("invalid field name `" + fieldName + "'"); assert refPath.getTargetTypes().iterator().next().isInstance(jobj); return this.tx.getKey(jobj.getObjId(), refPath.targetFieldStorageId); }
/** * Quick lookup for the {@link JField} corresponding to the given object and field storage ID. * * @param id object ID * @param storageId field storage ID * @param <T> expected field type * @return list of {@link JClass}es whose type is {@code type} or a sub-type, ordered by storage ID * @throws TypeNotInSchemaVersionException if {@code id} has a type that does not exist in this instance's schema version * @throws UnknownFieldException if {@code storageId} does not correspond to any field in the object's type */ @SuppressWarnings("unchecked") <T extends JField> T getJField(ObjId id, int storageId, Class<T> type) { final JField jfield = this.typeFieldMap.get(this.getTypeFieldKey(id.getStorageId(), storageId)); if (jfield == null) { this.getJClass(id.getStorageId()).getJField(storageId, type); // should always throw the appropriate exception assert false; } try { return type.cast(jfield); } catch (ClassCastException e) { throw new UnknownFieldException(storageId, jfield + "' is not a " + type.getSimpleName().replaceAll("^J(.*)Field$", "").toLowerCase() + " field"); } }
/** * Create the underlying {@link JTransaction} for a new transaction. * * <p> * The implementation in {@link PermazenTransactionManager} just delegates to * {@link Permazen#createTransaction(boolean, ValidationMode, Map)} using this instance's configured * settings for validation mode and allowing new schema versions. * * @param options transaction options * @return newly created {@link JTransaction} * @throws DatabaseException if an error occurs */ protected JTransaction createTransaction(Map<String, Object> options) { return this.jdb.createTransaction(this.allowNewSchema, this.validationMode, options); }
/** * Constructor. * * @param jdb underlying database * @param type type restrictions, or null for none */ public TypeContainer(Permazen jdb, Class<?> type) { super(Node.class); Preconditions.checkArgument(jdb != null, "null jdb"); this.jdb = jdb; // Get the types of all JClasses assignable to the given type, and use lowest common ancestor as the "top type" final HashSet<Class<?>> types = this.jdb.getJClasses().values().stream() .filter(jclass -> type == null || type.isAssignableFrom(jclass.getType())) .map(JClass::getType) .collect(Collectors.toCollection(HashSet::new)); this.rootType = !types.isEmpty() ? Util.findLowestCommonAncestorOfClasses(types).getRawType() : Object.class; }
"a Permazen transaction is already open in the current thread"); if (this.schemaVersion != 0) this.jdb.setConfiguredVersion(this.schemaVersion); final JTransaction jtx = this.jdb.createTransaction(this.allowNewSchema, this.validationMode != null ? this.validationMode : ValidationMode.AUTOMATIC, options); JTransaction.setCurrent(jtx);
/** * Get a {@link NameIndex} based on {@linkplain #getSchemaModel this instance's schema model}. * * @return a name index on this instance's schema model */ public NameIndex getNameIndex() { if (this.nameIndex == null) this.nameIndex = new NameIndex(this.getSchemaModel()); return this.nameIndex; }
/** * Constructor for {@link SessionMode#PERMAZEN}. * * @param jdb database * @throws IllegalArgumentException if {@code jdb} is null */ public Session(Permazen jdb) { this(jdb, jdb != null ? jdb.getDatabase() : null, jdb != null ? jdb.getDatabase().getKVDatabase() : null); }
@Override protected boolean supports(Class<?> target) { return this.jdb.findJClass(target) != null; }
final Class<?> startType = this.jdb.getJClass(id).type; final ArrayList<int[]> pathReferencesList = new ArrayList<>(refPaths.length); for (String refPath : refPaths) { final ReferencePath path = this.jdb.parseReferencePath(startType, refPath, false, true);
/** * Create a new transaction. * * <p> * Convenience method; equivalent to: * <blockquote><pre> * {@link #createTransaction(boolean, ValidationMode, Map) createTransaction}(allowNewSchema, validationMode, null) * </pre></blockquote> * * @param allowNewSchema whether creating a new schema version is allowed * @param validationMode the {@link ValidationMode} to use for the new transaction * @return the newly created transaction * @throws io.permazen.core.InvalidSchemaException if the schema does not match what's recorded in the * database for the schema version provided to the constructor * @throws io.permazen.core.InvalidSchemaException if the schema version provided to the constructor * is not recorded in the database and {@code allowNewSchema} is false * @throws io.permazen.core.InvalidSchemaException if the schema version provided to the constructor * is not recorded in the database and {@code allowNewSchema} is true, but the schema is incompatible * with one or more previous schemas alread recorded in the database (i.e., the same storage ID is used * incompatibly between schema versions) * @throws io.permazen.core.InconsistentDatabaseException if inconsistent or invalid meta-data is detected in the database * @throws IllegalArgumentException if {@code validationMode} is null */ public JTransaction createTransaction(boolean allowNewSchema, ValidationMode validationMode) { return this.createTransaction(allowNewSchema, validationMode, null); }
KeyRanges[] getPathKeyRanges() { if (this.pathKeyRanges == null) { final int numJClasses = this.jdb.jclasses.size(); final KeyRanges[] array = new KeyRanges[this.pathTypes.size()]; for (int i = 0; i < this.pathTypes.size(); i++) { final HashSet<JClass<?>> jclasses = new HashSet<>(); final Set<Class<?>> types = this.pathTypes.get(i); for (Class<?> type : types) jclasses.addAll(this.jdb.getJClasses(type)); if (jclasses.size() == numJClasses && this.isAnyAssignableFrom(types, UntypedJObject.class)) continue; // no filter needed final ArrayList<KeyRange> ranges = new ArrayList<>(jclasses.size()); for (JClass<?> jclass : jclasses) ranges.add(ObjId.getKeyRange(jclass.storageId)); array[i] = new KeyRanges(ranges); } this.pathKeyRanges = array; } return this.pathKeyRanges; }
/** * Create a new {@link SnapshotJTransaction} based on the provided key/value store. * * <p> * The key/value store will be initialized if necessary (i.e., {@code kvstore} may be empty), otherwise it will be * validated against the schema information associated with this instance. * * <p> * The returned {@link SnapshotJTransaction} does not support {@link SnapshotJTransaction#commit commit()} or * {@link SnapshotJTransaction#rollback rollback()}, and can be used indefinitely. * * @param kvstore key/value store, empty or having content compatible with this transaction's {@link Permazen} * @param allowNewSchema whether creating a new schema version in {@code kvstore} is allowed * @param validationMode the {@link ValidationMode} to use for the snapshot transaction * @return snapshot transaction based on {@code kvstore} * @throws io.permazen.core.SchemaMismatchException if {@code kvstore} contains incompatible or missing schema information * @throws io.permazen.core.InconsistentDatabaseException if inconsistent or invalid meta-data is detected in the database * @throws IllegalArgumentException if {@code kvstore} is null */ public SnapshotJTransaction createSnapshotTransaction(KVStore kvstore, boolean allowNewSchema, ValidationMode validationMode) { final SnapshotTransaction stx = this.db.createSnapshotTransaction(kvstore, this.getSchemaModel(), this.configuredVersion, allowNewSchema); return new SnapshotJTransaction(this, stx, validationMode); }
@Override public Field<?> caseJCounterField(JCounterField jfield) { final TextField field = new TextField(); field.setWidth("100%"); field.setNullSettingAllowed(false); field.setConverter(new SimpleFieldConverter<Long>(JObjectEditorWindow.this.jclass.getPermazen() .getDatabase().getFieldTypeRegistry().getFieldType(TypeToken.of(long.class)))); return field; } @Override
/** * Constructor. * * <p> * Uses a default {@code storageIdMapper} that creates new instances for imported objects via * {@link JTransaction#create(Class)}, using the Permazen model type found by * {@link Permazen#findJClass(Class)} when given the POJO's class. * * @param jtx the transaction in which to import objects * @throws IllegalArgumentException if {@code jtx} is null */ public ImportContext(JTransaction jtx) { Preconditions.checkArgument(jtx != null); this.jtx = jtx; this.storageIdMapper = obj -> { final JClass<?> modelClass = this.jtx.jdb.findJClass(obj.getClass()); if (modelClass == null) throw new IllegalArgumentException("no Permazen model class corresponds to POJO " + obj.getClass()); return ((JObject)this.jtx.create(modelClass)).getObjId(); }; }