use of org.structr.schema.compiler.NodeExtender in project structr by structr.
the class SchemaService method reloadSchema.
public static boolean reloadSchema(final ErrorBuffer errorBuffer, final String initiatedBySessionId) {
final ConfigurationProvider config = StructrApp.getConfiguration();
final App app = StructrApp.getInstance();
boolean success = true;
// compiling must only be done once
if (compiling.compareAndSet(false, true)) {
try {
final Map<String, Map<String, PropertyKey>> removedClasses = new HashMap<>(config.getTypeAndPropertyMapping());
final Map<String, GraphQLType> graphQLTypes = new LinkedHashMap<>();
final NodeExtender nodeExtender = new NodeExtender(initiatedBySessionId);
final Set<String> dynamicViews = new LinkedHashSet<>();
try (final Tx tx = app.tx()) {
// collect auto-generated schema nodes
SchemaService.ensureBuiltinTypesExist();
// add schema nodes from database
for (final SchemaNode schemaInfo : app.nodeQuery(SchemaNode.class).getAsList()) {
schemaInfo.handleMigration();
final String sourceCode = SchemaHelper.getSource(schemaInfo, errorBuffer);
if (sourceCode != null) {
final String className = schemaInfo.getClassName();
// only load dynamic node if there were no errors while generating
// the source code (missing modules etc.)
nodeExtender.addClass(className, sourceCode);
dynamicViews.addAll(schemaInfo.getDynamicViews());
// initialize GraphQL engine as well
schemaInfo.initializeGraphQL(graphQLTypes);
}
}
// collect relationship classes
for (final SchemaRelationshipNode schemaRelationship : app.nodeQuery(SchemaRelationshipNode.class).getAsList()) {
nodeExtender.addClass(schemaRelationship.getClassName(), schemaRelationship.getSource(errorBuffer));
dynamicViews.addAll(schemaRelationship.getDynamicViews());
// initialize GraphQL engine as well
schemaRelationship.initializeGraphQL(graphQLTypes);
}
// this is a very critical section :)
synchronized (SchemaService.class) {
// clear propagating relationship cache
SchemaRelationshipNode.clearPropagatingRelationshipTypes();
// compile all classes at once and register
final Map<String, Class> newTypes = nodeExtender.compile(errorBuffer);
for (final Class newType : newTypes.values()) {
// instantiate classes to execute static initializer of helpers
try {
// do full reload
config.registerEntityType(newType);
newType.newInstance();
} catch (Throwable ignore) {
}
}
// calculate difference between previous and new classes
removedClasses.keySet().removeAll(StructrApp.getConfiguration().getTypeAndPropertyMapping().keySet());
}
// create properties and views etc.
for (final SchemaNode schemaNode : app.nodeQuery(SchemaNode.class).getAsList()) {
schemaNode.createBuiltInSchemaEntities(errorBuffer);
}
success = !errorBuffer.hasError();
if (success) {
// prevent inheritance map from leaking
SearchCommand.clearInheritanceMap();
AccessPathCache.invalidate();
// clear relationship instance cache
AbstractNode.clearRelationshipTemplateInstanceCache();
// clear permission cache
AbstractNode.clearPermissionResolutionCache();
// inject views in configuration provider
config.registerDynamicViews(dynamicViews);
if (Services.calculateHierarchy() || !Services.isTesting()) {
calculateHierarchy();
}
if (Services.updateIndexConfiguration() || !Services.isTesting()) {
updateIndexConfiguration(removedClasses);
}
tx.success();
final GraphQLObjectType.Builder queryTypeBuilder = GraphQLObjectType.newObject();
// register types in "Query" type
for (final Entry<String, GraphQLType> entry : graphQLTypes.entrySet()) {
final String className = entry.getKey();
final GraphQLType type = entry.getValue();
// register type in query type
queryTypeBuilder.field(GraphQLFieldDefinition.newFieldDefinition().name(className).type(new GraphQLList(type)).argument(GraphQLArgument.newArgument().name("id").type(Scalars.GraphQLString).build()).argument(GraphQLArgument.newArgument().name("type").type(Scalars.GraphQLString).build()).argument(GraphQLArgument.newArgument().name("name").type(Scalars.GraphQLString).build()).argument(GraphQLArgument.newArgument().name("_page").type(Scalars.GraphQLInt).build()).argument(GraphQLArgument.newArgument().name("_pageSize").type(Scalars.GraphQLInt).build()).argument(GraphQLArgument.newArgument().name("_sort").type(Scalars.GraphQLString).build()).argument(GraphQLArgument.newArgument().name("_desc").type(Scalars.GraphQLBoolean).build()));
}
// exchange graphQL schema after successful build
synchronized (SchemaService.class) {
graphQLSchema = GraphQLSchema.newSchema().query(queryTypeBuilder.name("Query").build()).build(new LinkedHashSet<>(graphQLTypes.values()));
}
}
} catch (FrameworkException fex) {
logger.error("Unable to compile dynamic schema: {}", fex.getMessage());
success = false;
errorBuffer.getErrorTokens().addAll(fex.getErrorBuffer().getErrorTokens());
} catch (Throwable t) {
t.printStackTrace();
logger.error("Unable to compile dynamic schema: {}", t.getMessage());
success = false;
}
if (!success) {
if (Settings.SchemAutoMigration.getValue()) {
// handle migration in separate transaction
try (final Tx tx = app.tx()) {
// try to handle certain errors automatically
handleAutomaticMigration(errorBuffer);
tx.success();
} catch (FrameworkException fex) {
}
} else {
logger.error("Unable to compile dynamic schema, and automatic migration is not enabled. Please set application.schema.automigration = true in structr.conf to enable modification of existing schema classes.");
}
}
} finally {
// compiling done
compiling.set(false);
}
}
return success;
}
Aggregations