/** * Returns the singleton application object matching the given {@code applicationClass} within * the given {@code database}. */ @SuppressWarnings("unchecked") public static <T extends Application> T getInstanceUsing( Class<T> applicationClass, Database database) { ObjectType type = database.getEnvironment().getTypeByClass(applicationClass); Query<T> query = Query.from(applicationClass).where("_type = ?", type.getId()).using(database); T app = query.first(); if (app == null) { DistributedLock lock = DistributedLock.Static.getInstance(database, applicationClass.getName()); lock.lock(); try { app = query.clone().noCache().first(); if (app == null) { app = (T) type.createObject(null); app.setName(type.getDisplayName()); app.saveImmediately(); return app; } } finally { lock.unlock(); } } String oldName = app.getName(); String newName = type.getDisplayName(); if (!ObjectUtils.equals(oldName, newName)) { app.setName(newName); app.save(); } return app; }
/** 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); } } } } }