@Override protected void setUp() throws Exception { init(); securityContext = SecurityContext.getSuperUserInstance(); createNodeCommand = Services.command(securityContext, CreateNodeCommand.class); createRelationshipCommand = Services.command(securityContext, CreateRelationshipCommand.class); deleteNodeCommand = Services.command(securityContext, DeleteNodeCommand.class); transactionCommand = Services.command(securityContext, TransactionCommand.class); graphDbCommand = Services.command(securityContext, GraphDatabaseCommand.class); findNodeCommand = Services.command(securityContext, FindNodeCommand.class); }
public static Object call(final String key, final Map<String, Object> parameters) throws FrameworkException { final SecurityContext superUserContext = SecurityContext.getSuperUserInstance(); final App app = StructrApp.getInstance(superUserContext); // we might want to introduce caching here at some point in the future.. // Cache can be invalidated when the schema is rebuilt for example.. final List<SchemaMethod> methods = app.nodeQuery(SchemaMethod.class).andName(key).getAsList(); if (methods.isEmpty()) { logger.debug("Tried to call method {} but no SchemaMethod entity was found.", key); } else { for (final SchemaMethod method : methods) { // only call methods that are NOT part of a schema node final AbstractSchemaNode entity = method.getProperty(SchemaMethod.schemaNode); if (entity == null) { final String source = method.getProperty(SchemaMethod.source); if (source != null) { return Actions.execute(superUserContext, null, "${" + source + "}", parameters); } else { logger.warn("Schema method {} has no source code, will NOT be executed.", key); } } else { logger.warn("Schema method {} is attached to an entity, will NOT be executed.", key); } } } return null; }
/** * This method checks all configured external authentication services. * * @param request * @param response * @return user */ protected static Principal checkExternalAuthentication( final HttpServletRequest request, final HttpServletResponse response) throws FrameworkException { final String path = PathHelper.clean(request.getPathInfo()); final String[] uriParts = PathHelper.getParts(path); logger.log(Level.FINE, "Checking external authentication ..."); if (uriParts == null || uriParts.length != 3 || !("oauth".equals(uriParts[0]))) { logger.log(Level.FINE, "Incorrect URI parts for OAuth process, need /oauth/<name>/<action>"); return null; } final String name = uriParts[1]; final String action = uriParts[2]; // Try to getValue an OAuth2 server for the given name final StructrOAuthClient oauthServer = StructrOAuthClient.getServer(name); if (oauthServer == null) { logger.log(Level.FINE, "No OAuth2 authentication server configured for {0}", path); return null; } if ("login".equals(action)) { try { response.sendRedirect(oauthServer.getEndUserAuthorizationRequestUri(request)); return null; } catch (Exception ex) { logger.log(Level.SEVERE, "Could not send redirect to authorization server", ex); } } else if ("auth".equals(action)) { final String accessToken = oauthServer.getAccessToken(request); final SecurityContext superUserContext = SecurityContext.getSuperUserInstance(); if (accessToken != null) { logger.log(Level.FINE, "Got access token {0}", accessToken); // securityContext.setAttribute("OAuthAccessToken", accessToken); String value = oauthServer.getCredential(request); logger.log(Level.FINE, "Got credential value: {0}", new Object[] {value}); if (value != null) { PropertyKey credentialKey = oauthServer.getCredentialKey(); Principal user = AuthHelper.getPrincipalForCredential(credentialKey, value); if (user == null && userAutoCreate) { user = RegistrationResource.createUser( superUserContext, credentialKey, value, true, userClass); } if (user != null) { AuthHelper.doLogin(request, user); HtmlServlet.setNoCacheHeaders(response); try { logger.log(Level.FINE, "Response status: {0}", response.getStatus()); response.sendRedirect(oauthServer.getReturnUri()); } catch (IOException ex) { logger.log( Level.SEVERE, "Could not redirect to {0}: {1}", new Object[] {oauthServer.getReturnUri(), ex}); } return user; } } } } try { response.sendRedirect(oauthServer.getErrorUri()); } catch (IOException ex) { logger.log( Level.SEVERE, "Could not redirect to {0}: {1}", new Object[] {oauthServer.getReturnUri(), ex}); } return null; }
/** * Examine request and try to find a user. * * <p>First, check session id, then try external (OAuth) authentication, finally, check standard * login by credentials. * * @param request * @param response * @return security context * @throws FrameworkException */ @Override public SecurityContext initializeAndExamineRequest( final HttpServletRequest request, final HttpServletResponse response) throws FrameworkException { SecurityContext securityContext; Principal user = checkSessionAuthentication(request); if (user == null) { user = checkExternalAuthentication(request, response); } if (user == null) { user = getUser(request, true); } if (user == null) { // If no user could be determined, assume frontend access securityContext = SecurityContext.getInstance(user, request, AccessMode.Frontend); } else { if (user instanceof SuperUser) { securityContext = SecurityContext.getSuperUserInstance(request); } else { securityContext = SecurityContext.getInstance(user, request, AccessMode.Backend); } } securityContext.setAuthenticator(this); // Check CORS settings (Cross-origin resource sharing, see // http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) final String origin = request.getHeader("Origin"); if (!StringUtils.isBlank(origin)) { final Services services = Services.getInstance(); response.setHeader("Access-Control-Allow-Origin", origin); // allow cross site resource sharing (read only) final String maxAge = services.getConfigurationValue(Services.ACCESS_CONTROL_MAX_AGE); if (StringUtils.isNotBlank(maxAge)) { response.setHeader("Access-Control-MaxAge", maxAge); } final String allowMethods = services.getConfigurationValue(Services.ACCESS_CONTROL_ALLOW_METHODS); if (StringUtils.isNotBlank(allowMethods)) { response.setHeader("Access-Control-Allow-Methods", allowMethods); } final String allowHeaders = services.getConfigurationValue(Services.ACCESS_CONTROL_ALLOW_HEADERS); if (StringUtils.isNotBlank(allowHeaders)) { response.setHeader("Access-Control-Allow-Headers", allowHeaders); } final String allowCredentials = services.getConfigurationValue(Services.ACCESS_CONTROL_ALLOW_CREDENTIALS); if (StringUtils.isNotBlank(allowCredentials)) { response.setHeader("Access-Control-Allow-Credentials", allowCredentials); } final String exposeHeaders = services.getConfigurationValue(Services.ACCESS_CONTROL_EXPOSE_HEADERS); if (StringUtils.isNotBlank(exposeHeaders)) { response.setHeader("Access-Control-Expose-Headers", exposeHeaders); } } examined = true; return securityContext; }
@Override public void execute(Map<String, Object> attributes) throws FrameworkException { final String entityType = (String) attributes.get("type"); final String relType = (String) attributes.get("relType"); final GraphDatabaseService graphDb = (GraphDatabaseService) arguments.get("graphDb"); final SecurityContext superUserContext = SecurityContext.getSuperUserInstance(); final NodeFactory nodeFactory = new NodeFactory(superUserContext); final RelationshipFactory relFactory = new RelationshipFactory(superUserContext); if (entityType != null) { final Class type = EntityContext.getEntityClassForRawType(entityType); if (type != null) { // final Result<AbstractNode> result = Services.command(securityContext, // SearchNodeCommand.class).execute(true, false, Search.andExactType(type.getSimpleName())); final Result<AbstractNode> result = nodeFactory.instantiateAll(GlobalGraphOperations.at(graphDb).getAllNodes()); final List<AbstractNode> nodes = new ArrayList<AbstractNode>(); for (AbstractNode node : result.getResults()) { if (node.getClass().equals(type)) { nodes.add(node); } } logger.log( Level.INFO, "Start (re-)indexing all nodes of type {0}", new Object[] {type.getSimpleName()}); long count = bulkGraphOperation( securityContext, nodes, 1000, "RebuildIndex", new BulkGraphOperation<AbstractNode>() { @Override public void handleGraphObject( SecurityContext securityContext, AbstractNode node) { node.updateInIndex(); } @Override public void handleThrowable( SecurityContext securityContext, Throwable t, AbstractNode node) { logger.log( Level.WARNING, "Unable to index node {0}: {1}", new Object[] {node, t.getMessage()}); } @Override public void handleTransactionFailure( SecurityContext securityContext, Throwable t) { logger.log(Level.WARNING, "Unable to index node: {0}", t.getMessage()); } }); logger.log(Level.INFO, "Done with (re-)indexing {0} nodes", count); return; } } else if (relType != null) { // final Result<AbstractNode> result = Services.command(securityContext, // SearchNodeCommand.class).execute(true, false, Search.andExactType(type.getSimpleName())); final List<AbstractRelationship> unfilteredRels = relFactory.instantiate(GlobalGraphOperations.at(graphDb).getAllRelationships()); final List<AbstractRelationship> rels = new ArrayList<AbstractRelationship>(); for (AbstractRelationship rel : unfilteredRels) { if (!rel.getType().equals(relType)) { rels.add(rel); } } logger.log(Level.INFO, "Start setting UUID on all rels of type {0}", new Object[] {relType}); long count = bulkGraphOperation( securityContext, rels, 1000, "SetRelationshipUuid", new BulkGraphOperation<AbstractRelationship>() { @Override public void handleGraphObject( SecurityContext securityContext, AbstractRelationship rel) { rel.updateInIndex(); } @Override public void handleThrowable( SecurityContext securityContext, Throwable t, AbstractRelationship rel) { logger.log( Level.WARNING, "Unable to index relationship {0}: {1}", new Object[] {rel, t.getMessage()}); } @Override public void handleTransactionFailure(SecurityContext securityContext, Throwable t) { logger.log(Level.WARNING, "Unable to index relationship: {0}", t.getMessage()); } }); logger.log(Level.INFO, "Done with (re-)indexing {0} relationships", count); return; } logger.log(Level.INFO, "Unable to determine entity type to re-index."); }
@Override public Object apply(final ActionContext ctx, final GraphObject entity, final Object[] sources) throws FrameworkException { if (arrayHasMinLengthAndMaxLengthAndAllElementsNotNull(sources, 1, 2)) { final String cacheKey = cacheKey(sources); String value = getCachedValue(cacheKey); if (value == null) { final SecurityContext superUserSecurityContext = SecurityContext.getSuperUserInstance(); final String locale = ctx.getLocale().toString(); final String name = sources[0].toString(); Query query = StructrApp.getInstance(superUserSecurityContext) .nodeQuery(Localization.class) .and(Localization.locale, locale) .and(Localization.name, name); List<Localization> localizations; final Locale ctxLocale = ctx.getLocale(); final String fullLocale = ctxLocale.toString(); final String lang = ctxLocale.getLanguage(); if (sources.length == 2) { final String domain = sources[1].toString(); // with domain query.and(Localization.domain, domain); localizations = query.getAsList(); if (localizations.isEmpty() && fullLocale.contains("_")) { // no language-specific localization found, try language code only query = StructrApp.getInstance(superUserSecurityContext) .nodeQuery(Localization.class) .and(Localization.locale, lang) .and(Localization.name, name) .and(Localization.domain, domain); localizations = query.getAsList(); } } else { // without domain query.blank(Localization.domain); localizations = query.getAsList(); if (localizations.isEmpty() && fullLocale.contains("_")) { // no language-specific localization found, try language code only query = StructrApp.getInstance(superUserSecurityContext) .nodeQuery(Localization.class) .and(Localization.locale, lang) .and(Localization.name, name) .blank(Localization.domain); localizations = query.getAsList(); } } if (localizations.size() > 1) { // Ambiguous localization found if (sources.length > 1) { logger.warn( "Found ambiguous localization for key \"{}\" and domain \"{}\". Please fix. Parameters: {}", new Object[] { sources[0].toString(), sources[1].toString(), getParametersAsString(sources) }); } else { logger.warn( "Found ambiguous localization for key \"{}\". Please fix. Parameters: {}", new Object[] {sources[0].toString(), getParametersAsString(sources)}); } } // return first localization if (localizations.isEmpty()) { // no localization found - return the key value = name; } else { value = localizations.get(0).getProperty(Localization.localizedName); } cacheValue(cacheKey, value); } return value; } else if (sources.length == 1 || sources.length == 2) { logParameterError(entity, sources, ctx.isJavaScriptContext()); // silently ignore null values return ""; } else { logParameterError(entity, sources, ctx.isJavaScriptContext()); // only show the error message for wrong parameter count return usage(ctx.isJavaScriptContext()); } }
@Override public SecurityContext initializeAndExamineRequest( HttpServletRequest request, HttpServletResponse response) throws FrameworkException { return SecurityContext.getSuperUserInstance(request); }
public static void analyzeSchema() { final App app = StructrApp.getInstance(); final FileBasedHashLongMap<NodeInfo> nodeIdMap = new FileBasedHashLongMap<>(userHome + File.separator + ".structrSchemaAnalyzer"); final GraphDatabaseService graphDb = app.command(GraphDatabaseCommand.class).execute(); final ConfigurationProvider configuration = Services.getInstance().getConfigurationProvider(); final Set<NodeInfo> nodeTypes = new LinkedHashSet<>(); final Set<RelationshipInfo> relationships = new LinkedHashSet<>(); final Map<String, SchemaNode> schemaNodes = new LinkedHashMap<>(); final Map<String, List<TypeInfo>> typeInfoTypeMap = new LinkedHashMap<>(); final List<TypeInfo> reducedTypeInfos = new LinkedList<>(); final List<TypeInfo> typeInfos = new LinkedList<>(); Iterator<Relationship> relIterator = null; Iterator<Node> nodeIterator = null; logger.log(Level.INFO, "Fetching all nodes iterator.."); try (final Tx tx = app.tx()) { nodeIterator = Iterables.filter( new StructrAndSpatialPredicate(false, false, true), GlobalGraphOperations.at(graphDb).getAllNodes()) .iterator(); tx.success(); } catch (FrameworkException fex) { fex.printStackTrace(); } logger.log(Level.INFO, "Starting to analyze nodes.."); NodeServiceCommand.bulkGraphOperation( SecurityContext.getSuperUserInstance(), nodeIterator, 100000, "Analyzing nodes", new BulkGraphOperation<Node>() { @Override public void handleGraphObject(final SecurityContext securityContext, final Node node) throws FrameworkException { final NodeInfo nodeInfo = new NodeInfo(node); // hashcode of nodeInfo is derived from its property and type signature! nodeTypes.add(nodeInfo); // add node ID to our new test datastructure nodeIdMap.add(nodeInfo, node.getId()); } }); logger.log(Level.INFO, "Identifying common base classes.."); try (final Tx tx = app.tx(true, false, false)) { // nodeTypes now contains all existing node types and their property sets identifyCommonBaseClasses(app, nodeTypes, nodeIdMap, typeInfos); tx.success(); } catch (FrameworkException fex) { fex.printStackTrace(); } logger.log(Level.INFO, "Collecting type information.."); try (final Tx tx = app.tx(true, false, false)) { // group type infos by type collectTypeInfos(typeInfos, typeInfoTypeMap); tx.success(); } catch (FrameworkException fex) { fex.printStackTrace(); } logger.log(Level.INFO, "Aggregating type information.."); try (final Tx tx = app.tx(true, false, false)) { // reduce type infos with more than one type reduceTypeInfos(typeInfoTypeMap, reducedTypeInfos); tx.success(); } catch (FrameworkException fex) { fex.printStackTrace(); } logger.log(Level.INFO, "Identifying property sets.."); try (final Tx tx = app.tx(true, false, false)) { // intersect property sets of type infos intersectPropertySets(reducedTypeInfos); tx.success(); } catch (FrameworkException fex) { fex.printStackTrace(); } logger.log(Level.INFO, "Sorting result.."); try (final Tx tx = app.tx(false, false, false)) { // sort type infos Collections.sort(reducedTypeInfos, new HierarchyComparator(false)); tx.success(); } catch (FrameworkException fex) { fex.printStackTrace(); } final Map<String, TypeInfo> reducedTypeInfoMap = new LinkedHashMap<>(); for (final TypeInfo info : reducedTypeInfos) { final String type = info.getPrimaryType(); // map TypeInfo to type for later use reducedTypeInfoMap.put(type, info); logger.log(Level.INFO, "Starting with setting of type and ID for type {0}", type); NodeServiceCommand.bulkGraphOperation( SecurityContext.getSuperUserInstance(), info.getNodeIds().iterator(), 10000, "Setting type and ID", new BulkGraphOperation<Long>() { @Override public void handleGraphObject(SecurityContext securityContext, Long nodeId) throws FrameworkException { final Node node = graphDb.getNodeById(nodeId); node.setProperty(GraphObject.id.dbName(), NodeServiceCommand.getNextUuid()); node.setProperty(GraphObject.type.dbName(), type); } }); } logger.log(Level.INFO, "Fetching all relationships iterator.."); try (final Tx tx = app.tx(false, false, false)) { relIterator = Iterables.filter( new StructrAndSpatialPredicate(false, false, true), GlobalGraphOperations.at(graphDb).getAllRelationships()) .iterator(); tx.success(); } catch (FrameworkException fex) { fex.printStackTrace(); } logger.log(Level.INFO, "Starting with analyzing relationships.."); NodeServiceCommand.bulkGraphOperation( SecurityContext.getSuperUserInstance(), relIterator, 10000, "Analyzing relationships", new BulkGraphOperation<Relationship>() { @Override public void handleGraphObject(SecurityContext securityContext, Relationship rel) throws FrameworkException { final Node startNode = rel.getStartNode(); final Node endNode = rel.getEndNode(); // make sure node has been successfully identified above if (startNode.hasProperty("type") && endNode.hasProperty("type")) { final String relationshipType = rel.getType().name(); final String startNodeType = (String) startNode.getProperty("type"); final String endNodeType = (String) endNode.getProperty("type"); relationships.add(new RelationshipInfo(startNodeType, endNodeType, relationshipType)); // create combined type on imported relationship if (startNodeType != null && endNodeType != null) { final String combinedType = getCombinedType(startNodeType, relationshipType, endNodeType); logger.log( Level.FINE, "Combined relationship type {0} found for rel type {1}, start node type {2}, end node type {3}", new Object[] {combinedType, relationshipType, startNodeType, endNodeType}); rel.setProperty(GraphObject.type.dbName(), combinedType); } // create ID on imported relationship rel.setProperty(GraphObject.id.dbName(), NodeServiceCommand.getNextUuid()); } } }); logger.log(Level.INFO, "Grouping relationships.."); // group relationships by type final Map<String, List<RelationshipInfo>> relTypeInfoMap = new LinkedHashMap<>(); for (final RelationshipInfo relInfo : relationships) { // final String relType = relInfo.getRelType(); final String combinedType = getCombinedType( relInfo.getStartNodeType(), relInfo.getRelType(), relInfo.getEndNodeType()); List<RelationshipInfo> infos = relTypeInfoMap.get(combinedType); if (infos == null) { infos = new LinkedList<>(); relTypeInfoMap.put(combinedType, infos); } infos.add(relInfo); } logger.log(Level.INFO, "Aggregating relationship information.."); final List<RelationshipInfo> reducedRelationshipInfos = new ArrayList<>(); if ("true" .equals( Services.getInstance() .getConfigurationValue("importer.inheritancedetection", "true"))) { // reduce relationship infos into one for (final List<RelationshipInfo> infos : relTypeInfoMap.values()) { reducedRelationshipInfos.addAll(reduceNodeTypes(infos, reducedTypeInfoMap)); } } else { reducedRelationshipInfos.addAll(relationships); } logger.log(Level.INFO, "Starting with schema node creation.."); NodeServiceCommand.bulkGraphOperation( SecurityContext.getSuperUserInstance(), reducedTypeInfos.iterator(), 100000, "Creating schema nodes", new BulkGraphOperation<TypeInfo>() { @Override public void handleGraphObject(SecurityContext securityContext, TypeInfo typeInfo) throws FrameworkException { final String type = typeInfo.getPrimaryType(); if (!"ReferenceNode".equals(type)) { final Map<String, Class> props = typeInfo.getPropertySet(); final PropertyMap propertyMap = new PropertyMap(); // add properties for (final Map.Entry<String, Class> propertyEntry : props.entrySet()) { final String propertyName = propertyEntry.getKey(); final Class propertyType = propertyEntry.getValue(); // handle array types differently String propertyTypeName = propertyType.getSimpleName(); if (propertyType.isArray()) { // remove "[]" from the end and append "Array" to match the appropriate parser propertyTypeName = propertyTypeName.substring(0, propertyTypeName.length() - 2).concat("Array"); } propertyMap.put(new StringProperty("_".concat(propertyName)), propertyTypeName); } // set node type which is in "name" property propertyMap.put(AbstractNode.name, type); // check if there is an existing Structr entity with the same type // and make the dynamic class extend the existing class if yes. final Class existingType = configuration.getNodeEntityClass(type); if (existingType != null) { propertyMap.put(SchemaNode.extendsClass, existingType.getName()); } else if (!typeInfo.getOtherTypes().isEmpty()) { // only the first supertype is supported propertyMap.put( SchemaNode.extendsClass, typeInfo.getSuperclass(reducedTypeInfoMap)); } final SchemaNode existingNode = app.nodeQuery(SchemaNode.class).andName(type).getFirst(); if (existingNode != null) { for (final Entry<PropertyKey, Object> entry : propertyMap.entrySet()) { existingNode.setProperty(entry.getKey(), entry.getValue()); } schemaNodes.put(type, existingNode); } else { // create schema node schemaNodes.put(type, app.create(SchemaNode.class, propertyMap)); } } } }); logger.log(Level.INFO, "Starting with schema relationship creation.."); NodeServiceCommand.bulkGraphOperation( SecurityContext.getSuperUserInstance(), reducedRelationshipInfos.iterator(), 100000, "Creating schema relationships", new BulkGraphOperation<RelationshipInfo>() { @Override public void handleGraphObject(SecurityContext securityContext, RelationshipInfo template) throws FrameworkException { final SchemaNode startNode = schemaNodes.get(template.getStartNodeType()); final SchemaNode endNode = schemaNodes.get(template.getEndNodeType()); final String relationshipType = template.getRelType(); final PropertyMap propertyMap = new PropertyMap(); propertyMap.put(SchemaRelationshipNode.sourceId, startNode.getUuid()); propertyMap.put(SchemaRelationshipNode.targetId, endNode.getUuid()); propertyMap.put(SchemaRelationshipNode.relationshipType, relationshipType); app.create(SchemaRelationshipNode.class, propertyMap); } }); logger.log(Level.INFO, "Starting with index rebuild.."); // rebuild index app.command(BulkRebuildIndexCommand.class).execute(Collections.EMPTY_MAP); }
@Override public void processMessage(WebSocketMessage webSocketData) { final Map<String, Object> nodeData = webSocketData.getNodeData(); final String parentId = (String) nodeData.get("parentId"); final String childContent = (String) nodeData.get("childContent"); final String pageId = webSocketData.getPageId(); nodeData.remove("parentId"); if (pageId != null) { // check for parent ID before creating any nodes if (parentId == null) { getWebSocket() .send( MessageBuilder.status() .code(422) .message("Cannot add node without parentId") .build(), true); return; } // check if parent node with given ID exists final DOMNode parentNode = getDOMNode(parentId); if (parentNode == null) { getWebSocket() .send(MessageBuilder.status().code(404).message("Parent node not found").build(), true); return; } final Document document = getPage(pageId); if (document != null) { final String tagName = (String) nodeData.get("tagName"); final App app = StructrApp.getInstance(); nodeData.remove("tagName"); try { app.beginTx(); DOMNode newNode; if (tagName != null && !tagName.isEmpty()) { newNode = (DOMNode) document.createElement(tagName); } else { newNode = (DOMNode) document.createTextNode("#text"); } // append new node to parent if (newNode != null) { parentNode.appendChild(newNode); for (Entry entry : nodeData.entrySet()) { String key = (String) entry.getKey(); Object val = entry.getValue(); PropertyKey propertyKey = StructrApp.getConfiguration() .getPropertyKeyForDatabaseName(newNode.getClass(), key); if (propertyKey != null) { try { Object convertedValue = val; PropertyConverter inputConverter = propertyKey.inputConverter(SecurityContext.getSuperUserInstance()); if (inputConverter != null) { convertedValue = inputConverter.convert(val); } // newNode.unlockReadOnlyPropertiesOnce(); newNode.setProperty(propertyKey, convertedValue); } catch (FrameworkException fex) { logger.log( Level.WARNING, "Unable to set node property {0} of node {1} to {2}: {3}", new Object[] {propertyKey, newNode.getUuid(), val, fex.getMessage()}); } } } // create a child text node if content is given if (StringUtils.isNotBlank(childContent)) { DOMNode childNode = (DOMNode) document.createTextNode(childContent); if (newNode != null) { newNode.appendChild(childNode); } } } app.commitTx(); } catch (DOMException dex) { // send DOM exception getWebSocket() .send(MessageBuilder.status().code(422).message(dex.getMessage()).build(), true); } catch (FrameworkException ex) { Logger.getLogger(CreateAndAppendDOMNodeCommand.class.getName()) .log(Level.SEVERE, null, ex); } finally { app.finishTx(); } } else { getWebSocket() .send(MessageBuilder.status().code(404).message("Page not found").build(), true); } } else { getWebSocket() .send( MessageBuilder.status() .code(422) .message("Cannot create node without pageId") .build(), true); } }