/** Creates an object represented by the given {@code typeId} and {@code id}. */ public Object createObject(UUID typeId, UUID id) { bootstrapOnce.ensure(); Class<?> objectClass = null; ObjectType type = null; if (typeId != null && !GLOBALS_ID.equals(id)) { if (typeId.equals(id)) { objectClass = ObjectType.class; } else { type = getTypeById(typeId); if (type != null) { objectClass = type.isAbstract() ? Record.class : type.getObjectClass(); } } } boolean hasClass = true; if (objectClass == null) { objectClass = Record.class; hasClass = false; } Object object = SubstitutionUtils.newInstance(objectClass); State state; try { state = State.getInstance(object); } catch (IllegalArgumentException error) { object = TypeDefinition.getInstance(Record.class).newInstance(); state = State.getInstance(object); hasClass = false; } state.setDatabase(getDatabase()); state.setId(id); state.setTypeId(typeId); if (type != null) { if (!hasClass) { for (ObjectField field : type.getFields()) { Object defaultValue = field.getDefaultValue(); if (defaultValue != null) { state.put(field.getInternalName(), defaultValue); } } } } return object; }
public static void writeContents( Database database, BootstrapPackage pkg, Set<ObjectType> additionalTypes, Writer writer, String projectName) throws IOException { boolean first = true; boolean needsObjectTypeMap = false; Set<ObjectType> typeMaps = new HashSet<ObjectType>(); ObjectType objType = database.getEnvironment().getTypeByClass(ObjectType.class); Set<ObjectType> exportTypes = new HashSet<ObjectType>(); Query<?> query = Query.fromAll().using(database); Set<ObjectType> allTypeMappableTypes = new HashSet<ObjectType>(); Set<UUID> concreteTypeIds = new HashSet<UUID>(); for (ObjectType type : database.getEnvironment().getTypes()) { if (!type.as(TypeData.class).getTypeMappableGroups().isEmpty() && type.as(TypeData.class).getTypeMappableUniqueKey() != null) { allTypeMappableTypes.add(type); } } // Package: writer.write(PACKAGE_NAME_HEADER + ": "); writer.write(pkg.getName()); writer.write('\n'); // Project: writer.write(PROJECT_HEADER + ": "); writer.write(projectName); writer.write('\n'); // Date: writer.write(DATE_HEADER + ": "); writer.write(new DateTime().toString("yyyy-MM-dd HH:mm:ss z")); writer.write('\n'); // Types: writer.write(TYPES_HEADER + ": "); if (pkg.isInit()) { writer.write(ALL_TYPES_HEADER_VALUE); writer.write('\n'); } else { exportTypes.addAll(getAllTypes(database, pkg)); exportTypes.addAll(getAllTypes(database, additionalTypes)); for (ObjectType typeMappableType : allTypeMappableTypes) { GETTYPEMAPPABLETYPE: for (ObjectType type : exportTypes) { for (String group : typeMappableType.as(TypeData.class).getTypeMappableGroups()) { if (type.getGroups().contains(group)) { typeMaps.add(typeMappableType); break GETTYPEMAPPABLETYPE; } } } } GETOBJECTTYPE: for (ObjectType type : exportTypes) { for (ObjectField field : type.getFields()) { if (field.getTypes().contains(objType)) { needsObjectTypeMap = true; break GETOBJECTTYPE; } } } for (ObjectType exportType : exportTypes) { String clsName = exportType.getObjectClassName(); if (!ObjectUtils.isBlank(clsName)) { for (ObjectType concreteType : database.getEnvironment().getTypesByGroup(clsName)) { if (!concreteType.isAbstract()) { concreteTypeIds.add(concreteType.getId()); } } } } if (exportTypes.contains(objType)) { if (!first) { writer.write(','); } else { first = false; } writer.write(objType.getInternalName()); } for (ObjectType type : exportTypes) { if (type.equals(objType)) { continue; } if (!first) { writer.write(','); } else { first = false; } writer.write(type.getInternalName()); } writer.write('\n'); } // Determine if there are any fields that need references followed Map<UUID, Map<String, ObjectType>> followReferences = new HashMap<UUID, Map<String, ObjectType>>(); if (!pkg.isInit()) { for (ObjectType type : exportTypes) { for (String fieldName : type.as(TypeData.class).getFollowReferencesFields()) { ObjectField field = type.getField(fieldName); if (field != null) { for (ObjectType fieldType : field.getTypes()) { if (!exportTypes.contains(fieldType)) { if (!followReferences.containsKey(type.getId())) { followReferences.put(type.getId(), new HashMap<String, ObjectType>()); } followReferences.get(type.getId()).put(fieldName, fieldType); } } } } } } if (!typeMaps.isEmpty()) { for (ObjectType typeMapType : typeMaps) { String clsName = typeMapType.getObjectClassName(); if (!ObjectUtils.isBlank(clsName)) { for (ObjectType type : database.getEnvironment().getTypesByGroup(clsName)) { concreteTypeIds.remove(type.getId()); } } } } // Mapping Types: if (pkg.isInit() || needsObjectTypeMap || !typeMaps.isEmpty()) { first = true; writer.write(TYPE_MAP_HEADER + ": "); if (pkg.isInit() || needsObjectTypeMap) { writer.write(objType.getInternalName()); writer.write("/internalName"); first = false; } for (ObjectType typeMapType : typeMaps) { if (!first) { writer.write(','); } else { first = false; } writer.write(typeMapType.getInternalName()); writer.write('/'); writer.write(typeMapType.as(TypeData.class).getTypeMappableUniqueKey()); } writer.write('\n'); } // Row Count: Long count = 0L; try { if (concreteTypeIds.isEmpty()) { count = Query.fromAll().using(database).noCache().count(); } else { for (UUID concreteTypeId : concreteTypeIds) { long concreteCount = Query.fromAll() .using(database) .noCache() .where("_type = ?", concreteTypeId) .count(); count = count + concreteCount; } if (needsObjectTypeMap) { long objectCount = Query.fromAll().using(database).noCache().where("_type = ?", objType).count(); count = count + objectCount; } if (!typeMaps.isEmpty()) { for (ObjectType typeMapType : typeMaps) { long typeMapCount = Query.fromAll().using(database).noCache().where("_type = ?", typeMapType).count(); count = count + typeMapCount; } } } writer.write(ROW_COUNT_HEADER + ": "); writer.write(ObjectUtils.to(String.class, count)); writer.write('\n'); } catch (RuntimeException e) { // Count query timed out. Just don't write the row count header. count = null; } // blank line between headers and data writer.write('\n'); writer.flush(); // ObjectType records first if (exportTypes.isEmpty() || exportTypes.contains(objType) || needsObjectTypeMap) { for (Object r : Query.fromType(objType) .using(database) .noCache() .resolveToReferenceOnly() .iterable(100)) { writer.write(ObjectUtils.toJson(((Recordable) r).getState().getSimpleValues(true))); writer.write('\n'); } } // Then other mapping types for (ObjectType typeMapType : typeMaps) { for (Object r : Query.fromType(typeMapType) .using(database) .noCache() .resolveToReferenceOnly() .iterable(100)) { writer.write(ObjectUtils.toJson(((Recordable) r).getState().getSimpleValues(true))); writer.write('\n'); } } // Then everything else if (pkg.isInit()) { concreteTypeIds.clear(); // should already be empty concreteTypeIds.add(null); } UUID lastTypeId = null; Set<UUID> seenIds = new HashSet<UUID>(); query.getOptions().put(SqlDatabase.USE_JDBC_FETCH_SIZE_QUERY_OPTION, false); for (UUID typeId : concreteTypeIds) { Query<?> concreteQuery = query.clone(); if (typeId != null) { concreteQuery.where("_type = ?", typeId); } for (Object o : concreteQuery.noCache().resolveToReferenceOnly().iterable(100)) { if (o instanceof Recordable) { Recordable r = (Recordable) o; writer.write(ObjectUtils.toJson(r.getState().getSimpleValues(true))); writer.write('\n'); if (!pkg.isInit()) { if (lastTypeId == null || !lastTypeId.equals(r.getState().getTypeId())) { seenIds.clear(); } else if (seenIds.size() > MAX_SEEN_REFERENCE_IDS_SIZE) { seenIds.clear(); } lastTypeId = r.getState().getTypeId(); Map<String, ObjectType> followReferencesFieldMap; if ((followReferencesFieldMap = followReferences.get(r.getState().getTypeId())) != null) { for (Map.Entry<String, ObjectType> entry : followReferencesFieldMap.entrySet()) { Object reference = r.getState().getRawValue(entry.getKey()); Set<UUID> referenceIds = new HashSet<UUID>(); if (reference instanceof Collection) { for (Object referenceObj : ((Collection<?>) reference)) { if (referenceObj instanceof Recordable) { UUID referenceUUID = ObjectUtils.to( UUID.class, ((Recordable) referenceObj).getState().getId()); if (referenceUUID != null) { if (!seenIds.contains(referenceUUID)) { referenceIds.add(referenceUUID); } } } } } else if (reference instanceof Recordable) { UUID referenceUUID = ObjectUtils.to(UUID.class, ((Recordable) reference).getState().getId()); if (referenceUUID != null) { if (!seenIds.contains(referenceUUID)) { referenceIds.add(referenceUUID); } } } if (!referenceIds.isEmpty()) { for (Object ref : Query.fromType(entry.getValue()) .noCache() .using(database) .where("_id = ?", referenceIds) .selectAll()) { if (ref instanceof Recordable) { Recordable refr = (Recordable) ref; seenIds.add(refr.getState().getId()); writer.write(ObjectUtils.toJson(refr.getState().getSimpleValues(true))); writer.write('\n'); } } } } } } } } } writer.flush(); }
/** Immediately refreshes all types using the backing database. */ public synchronized void refreshTypes() { bootstrapOnce.ensure(); Database database = getDatabase(); try { TypesCache temporaryTypes = temporaryTypesLocal.get(); if (temporaryTypes == null) { temporaryTypes = new TypesCache(); temporaryTypesLocal.set(temporaryTypes); } List<ObjectType> types = Query.from(ObjectType.class).using(database).selectAll(); int typesSize = types.size(); LOGGER.info("Loading [{}] types from [{}]", typesSize, database.getName()); // Load all types from the database first. for (ObjectType type : types) { type.getFields().size(); // Pre-fetch. temporaryTypes.add(type); } if (initializeClasses) { // Make sure that the root type exists. ObjectType rootType = getRootType(); State rootTypeState; if (rootType != null) { rootTypeState = rootType.getState(); } else { rootType = new ObjectType(); rootTypeState = rootType.getState(); rootTypeState.setDatabase(database); } Map<String, Object> rootTypeOriginals = rootTypeState.getSimpleValues(); UUID rootTypeId = rootTypeState.getId(); rootTypeState.setTypeId(rootTypeId); rootTypeState.clear(); rootType.setObjectClassName(ObjectType.class.getName()); rootType.initialize(); temporaryTypes.add(rootType); try { database.beginWrites(); // Make the new root type available to other types. temporaryTypes.add(rootType); if (rootTypeState.isNew()) { State globals = getGlobals(); globals.put(ROOT_TYPE_FIELD, rootType); globals.save(); } else if (!rootTypeState.getSimpleValues().equals(rootTypeOriginals)) { temporaryTypes.changed.add(rootTypeId); } Set<Class<? extends Recordable>> objectClasses = ClassFinder.findClasses(Recordable.class); for (Iterator<Class<? extends Recordable>> i = objectClasses.iterator(); i.hasNext(); ) { Class<? extends Recordable> objectClass = i.next(); try { if (objectClass.isAnonymousClass() || Substitution.class.isAssignableFrom(objectClass)) { i.remove(); } } catch (IncompatibleClassChangeError error) { i.remove(); } } Set<Class<?>> globalModifications = new HashSet<Class<?>>(); Map<ObjectType, List<Class<?>>> typeModifications = new HashMap<ObjectType, List<Class<?>>>(); // Make sure all types are accessible to the rest of the // system as soon as possible, so that references can be // resolved properly later. for (Class<?> objectClass : objectClasses) { ObjectType type = getTypeByClass(objectClass); if (type == null) { type = new ObjectType(); type.getState().setDatabase(database); } else { type.getState().clear(); } type.setObjectClassName(objectClass.getName()); typeModifications.put(type, new ArrayList<Class<?>>()); temporaryTypes.add(type); } // Separate out all modifications from regular types. for (Class<?> objectClass : objectClasses) { if (!Modification.class.isAssignableFrom(objectClass)) { continue; } @SuppressWarnings("unchecked") Set<Class<?>> modifiedClasses = Modification.Static.getModifiedClasses( (Class<? extends Modification<?>>) objectClass); if (modifiedClasses.contains(Object.class)) { globalModifications.add(objectClass); continue; } for (Class<?> modifiedClass : modifiedClasses) { List<Class<?>> assignableClasses = new ArrayList<Class<?>>(); for (Class<?> c : objectClasses) { if (modifiedClass.isAssignableFrom(c)) { assignableClasses.add(c); } } for (Class<?> assignableClass : assignableClasses) { ObjectType type = getTypeByClass(assignableClass); if (type != null) { List<Class<?>> modifications = typeModifications.get(type); if (modifications == null) { modifications = new ArrayList<Class<?>>(); typeModifications.put(type, modifications); } modifications.add(objectClass); } } } } // Apply global modifications. for (Class<?> modification : globalModifications) { ObjectType.modifyAll(database, modification); } // Initialize all types. List<Class<?>> rootTypeModifications = typeModifications.remove(rootType); initializeAndModify(temporaryTypes, rootType, rootTypeModifications); if (rootTypeModifications != null) { for (Class<?> modification : rootTypeModifications) { ObjectType t = getTypeByClass(modification); initializeAndModify(temporaryTypes, t, typeModifications.remove(t)); } } ObjectType fieldType = getTypeByClass(ObjectField.class); List<Class<?>> fieldModifications = typeModifications.remove(fieldType); initializeAndModify(temporaryTypes, fieldType, fieldModifications); if (fieldModifications != null) { for (Class<?> modification : fieldModifications) { ObjectType t = getTypeByClass(modification); initializeAndModify(temporaryTypes, t, typeModifications.remove(t)); } } for (Map.Entry<ObjectType, List<Class<?>>> entry : typeModifications.entrySet()) { initializeAndModify(temporaryTypes, entry.getKey(), entry.getValue()); } database.commitWrites(); } finally { database.endWrites(); } } // Merge temporary types into new permanent types. TypesCache newPermanentTypes = new TypesCache(); for (ObjectType type : permanentTypes.byId.values()) { newPermanentTypes.add(type); } for (ObjectType type : temporaryTypes.byId.values()) { newPermanentTypes.add(type); } newPermanentTypes.changed.addAll(temporaryTypes.changed); newPermanentTypes.changed.addAll(permanentTypes.changed); // If any types changed, clear all types' extras. if (!temporaryTypes.changed.isEmpty()) { for (ObjectType type : newPermanentTypes.byId.values()) { type.getState().getExtras().clear(); } } permanentTypes = newPermanentTypes; lastTypesUpdate = new Date(); } finally { temporaryTypesLocal.remove(); } ObjectType singletonType = getTypeByClass(Singleton.class); if (singletonType != null) { for (ObjectType type : singletonType.findConcreteTypes()) { if (!Query.fromType(type).where("_type = ?", type).master().noCache().hasMoreThan(0)) { try { State.getInstance(type.createObject(null)).saveImmediately(); } catch (Exception error) { LOGGER.warn(String.format("Can't save [%s] singleton!", type.getLabel()), error); } } } } for (ObjectType type : getTypes()) { Class<?> objectClass = type.getObjectClass(); if (objectClass != null) { TypePostProcessorClasses tppcAnnotation = objectClass.getAnnotation(TypePostProcessorClasses.class); if (tppcAnnotation != null) { for (Class<? extends ObjectType.PostProcessor> processorClass : tppcAnnotation.value()) { ObjectType.PostProcessor processor = (ObjectType.PostProcessor) TYPE_POST_PROCESSORS.getUnchecked(processorClass); processor.process(type); } } } } }
public static void checkConsistency( Database database, BootstrapPackage pkg, Set<BootstrapPackage> allPackages, Set<ObjectType> additionalTypes) { Set<ObjectType> allMyTypes = getAllTypes(database, pkg); Set<ObjectType> allTypes = new HashSet<ObjectType>(); for (BootstrapPackage otherPkg : allPackages) { allTypes.addAll(getAllTypes(database, otherPkg)); } Map<ObjectField, ObjectType> checkFields = new HashMap<ObjectField, ObjectType>(); for (ObjectType type : allMyTypes) { for (ObjectField field : type.getFields()) { for (ObjectType t : field.getTypes()) { if (!field.isEmbedded() && !t.isEmbedded()) { checkFields.put(field, t); } } } } if (additionalTypes != null) { for (ObjectType type : additionalTypes) { for (ObjectField field : type.getFields()) { for (ObjectType t : field.getTypes()) { if (!field.isEmbedded() && !t.isEmbedded()) { checkFields.put(field, t); } } } } } for (ObjectField field : database.getEnvironment().getFields()) { for (ObjectType t : field.getTypes()) { if (!field.isEmbedded() && !t.isEmbedded()) { checkFields.put(field, t); } } } for (Map.Entry<ObjectField, ObjectType> entry : checkFields.entrySet()) { ObjectField field = entry.getKey(); ObjectType t = entry.getValue(); if (field.getParentType() != null && field .getParentType() .as(TypeData.class) .getFollowReferencesFields() .contains(field.getInternalName())) { continue; } if (!allMyTypes.contains(t)) { if (allTypes.contains(t)) { if (!pkg.getTypesInOtherPackages().containsKey(t)) { pkg.getTypesInOtherPackages().put(t, new HashSet<BootstrapPackage>()); } for (BootstrapPackage otherPkg : allPackages) { if (getAllTypes(database, otherPkg).contains(t)) { pkg.getTypesInOtherPackages().get(t).add(otherPkg); } } } if (t.as(TypeData.class).getTypeMappableGroups().isEmpty() || t.as(TypeData.class).getTypeMappableUniqueKey() == null) { if (!pkg.getMissingTypes().containsKey(t)) { pkg.getMissingTypes().put(t, new HashSet<ObjectField>()); } pkg.getMissingTypes().get(t).add(field); } } } }