OrientDBRepoService.java revision 39a2b1613dd6d8f45a32a9efda82fd0aead4cf43
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff/*
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Copyright (c) 2011-2013 ForgeRock AS. All Rights Reserved
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * The contents of this file are subject to the terms
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * of the Common Development and Distribution License
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * (the License). You may not use this file except in
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * compliance with the License.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * You can obtain a copy of the License at
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * http://forgerock.org/license/CDDLv1.0.html
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * See the License for the specific language governing
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * permission and limitations under the License.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * When distributing Covered Code, include this CDDL
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Header Notice in each file and include the License file
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * at http://forgerock.org/license/CDDLv1.0.html
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * If applicable, add the following below the CDDL Header,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * with the fields enclosed by brackets [] replaced by
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * your own identifying information:
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * "Portions Copyrighted [year] [name of copyright owner]"
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffpackage org.forgerock.openidm.repo.orientdb.impl;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport java.io.File;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport java.util.ArrayList;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport java.util.HashMap;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport java.util.List;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport java.util.Map;
59e1ef1c5c1b51a036ace7fc1da0d3e2da6ef0b0Andi Egloffimport java.util.UUID;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.apache.felix.scr.annotations.Activate;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.apache.felix.scr.annotations.Component;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.apache.felix.scr.annotations.ConfigurationPolicy;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.apache.felix.scr.annotations.Deactivate;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.apache.felix.scr.annotations.Modified;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.apache.felix.scr.annotations.Properties;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.apache.felix.scr.annotations.Property;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.apache.felix.scr.annotations.Service;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.fluent.JsonValue;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.fluent.JsonValueException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ActionRequest;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.BadRequestException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ConflictException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.CreateRequest;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.DeleteRequest;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ForbiddenException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.InternalServerErrorException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.NotFoundException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.NotSupportedException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.PatchRequest;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.PreconditionFailedException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.QueryRequest;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.QueryResult;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.QueryResultHandler;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ReadRequest;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.RequestHandler;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.Resource;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ResourceException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ResultHandler;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ServerContext;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.UpdateRequest;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.config.enhanced.EnhancedConfig;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.config.enhanced.JSONEnhancedConfig;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.core.IdentityServer;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.openidm.core.ServerConstants;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.repo.QueryConstants;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.repo.RepoBootService;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.repo.RepositoryService;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.repo.orientdb.impl.query.PredefinedQueries;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.repo.orientdb.impl.query.Queries;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.osgi.service.component.ComponentContext;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.slf4j.Logger;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.slf4j.LoggerFactory;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienle
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport com.orientechnologies.orient.core.db.document.ODatabaseDocumentPool;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport com.orientechnologies.orient.core.exception.OConcurrentModificationException;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport com.orientechnologies.orient.core.exception.ODatabaseException;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport com.orientechnologies.orient.core.index.OIndexException;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport com.orientechnologies.orient.core.record.impl.ODocument;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff/**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Repository service implementation using OrientDB
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @author aegloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff@Component(name = OrientDBRepoService.PID, immediate=true, policy=ConfigurationPolicy.REQUIRE, enabled=true)
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff@Service (value = {RepositoryService.class, RequestHandler.class}) // Omit the RepoBootService interface from the managed service
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff@Properties({
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Property(name = "service.description", value = "Repository Service using OrientDB"),
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Property(name = "service.vendor", value = ServerConstants.SERVER_VENDOR_NAME),
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Property(name = ServerConstants.ROUTER_PREFIX, value = "/repo/*") }) // "/repo/{partition}*") })
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffpublic class OrientDBRepoService implements RequestHandler, RepositoryService, RepoBootService {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final static Logger logger = LoggerFactory.getLogger(OrientDBRepoService.class);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String PID = "org.forgerock.openidm.repo.orientdb";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // Keys in the JSON configuration
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_QUERIES = "queries";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_DB_URL = "dbUrl";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_USER = "user";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_PASSWORD = "password";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_DB_STRUCTURE = "dbStructure";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_ORIENTDB_CLASS = "orientdbClass";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_INDEX = "index";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_PROPERTY_NAME = "propertyName";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_PROPERTY_NAMES = "propertyNames";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_PROPERTY_TYPE = "propertyType";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static final String CONFIG_INDEX_TYPE = "indexType";
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODatabaseDocumentPool pool;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff String dbURL;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff String user;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff String password;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff int poolMinSize = 5;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff int poolMaxSize = 20;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // Current configuration
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff JsonValue existingConfig;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // TODO: evaluate use of Guice instead
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff PredefinedQueries predefinedQueries = new PredefinedQueries();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff Queries queries = new Queries();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff EnhancedConfig enhancedConfig = new JSONEnhancedConfig();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff EmbeddedOServerService embeddedServer;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleRead(final ServerContext context, final ReadRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final ResultHandler<Resource> handler) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff handleRead2(context, request, handler);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (Exception ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff handler.handleError(adapt(ex));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleCreate(final ServerContext context, final CreateRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final ResultHandler<Resource> handler) {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff handleCreate2(context, request, handler);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (Exception ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff handler.handleError(adapt(ex));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleUpdate(ServerContext context, UpdateRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ResultHandler<Resource> handler) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff handleUpdate2(context, request, handler);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (Exception ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff handler.handleError(adapt(ex));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleDelete(final ServerContext context, final DeleteRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final ResultHandler<Resource> handler) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff handleDelete2(context, request, handler);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (Exception ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff handler.handleError(adapt(ex));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleQuery(final ServerContext context, final QueryRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final QueryResultHandler handler) {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff try {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff handleQuery2(context, request, handler);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (Exception ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff handler.handleError(adapt(ex));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * <p>
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * The object will contain metadata properties, including object identifier
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * {@code _id}, and object version {@code _rev} to enable optimistic
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * concurrency supported by OrientDB and OpenIDM.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param request
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * the identifier of the object to retrieve from the object set.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws NotFoundException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if the specified object could not be found.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ForbiddenException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if access to the object is forbidden.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws BadRequestException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if the passed identifier is invalid
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @return the requested object.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //@Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleRead2(final ServerContext context, final ReadRequest request,
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle final ResultHandler<Resource> handler) throws ResourceException {
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle Resource result = read(request);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle handler.handleResult(result);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle }
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle public Resource read(ReadRequest request) throws ResourceException {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller if (request.getResourceNameObject().size() < 2) {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller throw new NotFoundException("The object identifier did not include sufficient information to determine the object type and identifier of the object to read: " + request.getResourceName());
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller final String type = request.getResourceNameObject().parent().toString();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller final String localId = request.getResourceNameObject().leaf();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff Resource result = null;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODatabaseDocumentTx db = getConnection();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODocument doc = predefinedQueries.getByID(localId, type, db);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (doc == null) {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller throw new NotFoundException("Object " + localId + " not found in " + type);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff result = DocumentUtil.toResource(doc);
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller logger.trace("Completed get for id: {} result: {}", request.getResourceName(), result);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle return result;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } finally {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (db != null) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff db.close();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Creates a new object in the object set.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * <p>
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * This method sets the {@code _id} property to the assigned identifier for the object,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * and the {@code _rev} property to the revised object version (For optimistic concurrency)
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param context
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * the client-generated identifier to use, or {@code null} if
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * server-generated identifier is requested.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param request
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * the contents of the object to create in the object set.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws NotFoundException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if the specified id could not be resolved.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ForbiddenException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if access to the object or object set is forbidden.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ConflictException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if an object with the same ID already exists.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //@Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleCreate2(final ServerContext context, final CreateRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final ResultHandler<Resource> handler) throws ResourceException {
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle Resource result = create(request);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle handler.handleResult(result);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle }
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle public Resource create(CreateRequest request) throws ResourceException {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller if (request.getResourceNameObject().isEmpty()) {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller throw new NotFoundException("The object identifier did not include sufficient information to determine the object type: " + request.getResourceName());
59e1ef1c5c1b51a036ace7fc1da0d3e2da6ef0b0Andi Egloff }
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller final String type = request.getResourceName();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller // TODO: should CREST support server side generation of ID itself?
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller final String localId = (request.getNewResourceId() == null || "".equals(request.getNewResourceId()))
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller ? UUID.randomUUID().toString() // Generate ID server side.
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller : request.getNewResourceId();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // Used currently for logging
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller String fullId = request.getResourceName() + "/" + localId;
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff String orientClassName = typeToOrientClassName(type);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff JsonValue obj = request.getContent();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff obj.put(DocumentUtil.TAG_ID, localId);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODatabaseDocumentTx db = getConnection();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try{
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // Rather than using MVCC for insert, rely on primary key uniqueness constraints to detect duplicate create
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODocument newDoc = DocumentUtil.toDocument(obj, null, db, orientClassName);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.trace("Created doc for id: {} to save {}", fullId, newDoc);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff newDoc.save();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff obj.put(DocumentUtil.TAG_REV, Integer.toString(newDoc.getVersion()));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.debug("Completed create for id: {} revision: {}", fullId, newDoc.getVersion());
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.trace("Create payload for id: {} doc: {}", fullId, newDoc);
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller return new Resource(obj.get(DocumentUtil.TAG_ID).asString(), obj.get(DocumentUtil.TAG_REV).asString(), obj);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (OIndexException ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // Because the OpenIDM ID is defined as unique, duplicate inserts must fail
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw new PreconditionFailedException("Create rejected as Object with same ID already exists. " + ex.getMessage(), ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (com.orientechnologies.orient.core.exception.ODatabaseException ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // Because the OpenIDM ID is defined as unique, duplicate inserts must fail.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // OrientDB may wrap the IndexException root cause.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (isCauseIndexException(ex, 10)) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw new PreconditionFailedException("Create rejected as Object with same ID already exists and was detected. "
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff + ex.getMessage(), ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } else {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw ex;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (RuntimeException e){
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw e;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } finally {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (db != null) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff db.close();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Updates the specified object in the object set.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * <p>
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller * This implementation does not require MVCC and uses the current revision if no revision
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienle * is specified in the request.
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller * <p>
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * If successful, this method updates metadata properties within the passed object,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * including: a new {@code _rev} value for the revised object's version
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param fullId the identifier of the object to be put, or {@code null} to request a generated identifier.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param rev the version of the object to update; or {@code null} if not provided.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param obj the contents of the object to put in the object set.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ConflictException if version is required but is {@code null}.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ForbiddenException if access to the object is forbidden.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws NotFoundException if the specified object could not be found.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws PreconditionFailedException if version did not match the existing object in the set.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws BadRequestException if the passed identifier is invalid
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //@Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleUpdate2(ServerContext context, UpdateRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ResultHandler<Resource> handler) throws ResourceException {
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle Resource result = update(request);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle handler.handleResult(result);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle }
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle public Resource update(UpdateRequest request) throws ResourceException {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller if (request.getResourceNameObject().size() < 2) {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller throw new NotFoundException("The object identifier did not include sufficient information to determine the object type and identifier of the object to update: " + request.getResourceName());
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller }
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller final String type = request.getResourceNameObject().parent().toString();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller final String localId = request.getResourceNameObject().leaf();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff String orientClassName = typeToOrientClassName(type);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff JsonValue obj = request.getNewContent();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller if (request.getRevision() != null && !"".equals(request.getRevision())) {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller obj.put(DocumentUtil.TAG_REV, request.getRevision());
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODatabaseDocumentTx db = getConnection();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try{
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODocument existingDoc = predefinedQueries.getByID(localId, type, db);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (existingDoc == null) {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller throw new NotFoundException("Update on object " + request.getResourceName() + " could not find existing object.");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODocument updatedDoc = DocumentUtil.toDocument(obj, existingDoc, db, orientClassName);
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller logger.trace("Updated doc for id {} to save {}", request.getResourceName(), updatedDoc);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff updatedDoc.save();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff obj.put(DocumentUtil.TAG_REV, Integer.toString(updatedDoc.getVersion()));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // Set ID to return to caller
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff obj.put(DocumentUtil.TAG_ID, updatedDoc.field(DocumentUtil.ORIENTDB_PRIMARY_KEY));
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller logger.debug("Committed update for id: {} revision: {}", request.getResourceName(), updatedDoc.getVersion());
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller logger.trace("Update payload for id: {} doc: {}", request.getResourceName(), updatedDoc);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle return new Resource(obj.get(DocumentUtil.TAG_ID).asString(),
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff obj.get(DocumentUtil.TAG_REV).asString(), obj);
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff } catch (ODatabaseException ex) {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff // Without transaction the concurrent modification exception gets nested instead
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff if (isCauseConcurrentModificationException(ex, 10)) {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff throw new PreconditionFailedException(
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff "Update rejected as current Object revision is different than expected by caller, the object has changed since retrieval: "
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff + ex.getMessage(), ex);
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff } else {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff throw ex;
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (OConcurrentModificationException ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw new PreconditionFailedException("Update rejected as current Object revision is different than expected by caller, the object has changed since retrieval: " + ex.getMessage(), ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (RuntimeException e){
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw e;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } finally {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (db != null) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff db.close();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Deletes the specified object from the object set.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * {@inheritDoc}
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws NotFoundException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if the specified object could not be found.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ForbiddenException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if access to the object is forbidden.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ConflictException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if version is required but is {@code null}.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws PreconditionFailedException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if version did not match the existing object in the set.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //@Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleDelete2(final ServerContext context, final DeleteRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final ResultHandler<Resource> handler) throws ResourceException {
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle Resource result = delete(request);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle handler.handleResult(result);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle }
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle public Resource delete(DeleteRequest request) throws ResourceException {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller if (request.getResourceNameObject().size() < 2) {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller throw new NotFoundException("The object identifier did not include sufficient information to determine the object type and identifier of the object to update: " + request.getResourceName());
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller if (request.getRevision() == null || "".equals(request.getRevision())) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw new ConflictException("Object passed into delete does not have revision it expects set.");
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller }
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller final String type = request.getResourceNameObject().parent().toString();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller final String localId = request.getResourceNameObject().leaf();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller int ver = DocumentUtil.parseVersion(request.getRevision()); // This throws ConflictException if parse fails
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODatabaseDocumentTx db = getConnection();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODocument existingDoc = predefinedQueries.getByID(localId, type, db);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (existingDoc == null) {
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller throw new NotFoundException("Object does not exist for delete on: " + request.getResourceName());
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff existingDoc.setVersion(ver); // State the version we expect to delete for MVCC check
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff db.delete(existingDoc);
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller logger.debug("delete for id succeeded: {} revision: {}", localId, request.getRevision());
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle return new Resource(localId, null, new JsonValue(null));
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff } catch (ODatabaseException ex) {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff // Without transaction the concurrent modification exception gets nested instead
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff if (isCauseConcurrentModificationException(ex, 10)) {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff throw new PreconditionFailedException(
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff "Delete rejected as current Object revision is different than expected by caller, the object has changed since retrieval. "
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff + ex.getMessage(), ex);
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff } else {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff throw ex;
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff }
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (OConcurrentModificationException ex) {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff throw new PreconditionFailedException(
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff "Delete rejected as current Object revision is different than expected by caller, the object has changed since retrieval."
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff + ex.getMessage(), ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (RuntimeException e){
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw e;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } finally {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (db != null) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff db.close();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handlePatch(final ServerContext context, final PatchRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final ResultHandler<Resource> handler) {
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff // TODO: impl
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff handler.handleError(new NotSupportedException("Patch not supported yet"));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleAction(final ServerContext context, final ActionRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final ResultHandler<JsonValue> handler) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff handler.handleError(new NotSupportedException("Action not supported"));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Performs the query on the specified object and returns the associated results.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * <p>
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Queries are parametric; a set of named parameters is provided as the query criteria.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * The query result is a JSON object structure composed of basic Java types.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * The returned map is structured as follow:
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * - The top level map contains meta-data about the query, plus an entry with the actual result records.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * - The <code>QueryConstants</code> defines the map keys, including the result records (QUERY_RESULT)
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param context
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * identifies the object to query.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param request
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * the parameters of the query to perform.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @return the query results, which includes meta-data and the result
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * records in JSON object structure format.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws NotFoundException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if the specified object could not be found.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws BadRequestException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if the specified params contain invalid arguments, e.g. a
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * query id that is not configured, a query expression that is
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * invalid, or missing query substitution tokens.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ForbiddenException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * if access to the object or specified query is forbidden.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //@Override
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public void handleQuery2(final ServerContext context, final QueryRequest request,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff final QueryResultHandler handler) throws ResourceException {
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle List<Resource> results = query(request);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle for (Resource result : results) {
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff handler.handleResource(result);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle }
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle handler.handleResult(new QueryResult());
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle }
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle public List<Resource> query(QueryRequest request) throws ResourceException {
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff List<Resource> results = new ArrayList<Resource>();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller logger.trace("Full id: {} Extracted type: {}", request.getResourceName(), request.getResourceName());
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // TODO: Statistics is not returned in result anymore
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // TODO: result is not needed in map form anymore
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff Map<String, Object> result = new HashMap<String, Object>();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODatabaseDocumentTx db = getConnection();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //List<Map<String, Object>> docs = new ArrayList<Map<String, Object>>();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //result.put(QueryConstants.QUERY_RESULT, docs);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff long start = System.currentTimeMillis();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller List<ODocument> queryResult = queries.query(request.getResourceName(), request, db);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff long end = System.currentTimeMillis();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (queryResult != null) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff long convStart = System.currentTimeMillis();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff for (ODocument entry : queryResult) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff Map<String, Object> convertedEntry = DocumentUtil.toMap(entry);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //docs.add(convertedEntry);
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle results.add(new Resource(
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff (String) convertedEntry.get(DocumentUtil.TAG_ID),
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff (String) convertedEntry.get(DocumentUtil.TAG_REV),
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff new JsonValue(convertedEntry)));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff long convEnd = System.currentTimeMillis();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff result.put(QueryConstants.STATISTICS_CONVERSION_TIME, Long.valueOf(convEnd-convStart));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff result.put(QueryConstants.STATISTICS_QUERY_TIME, Long.valueOf(end-start));
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (logger.isDebugEnabled()) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.debug("Query result contains {} records, took {} ms and took {} ms to convert result.",
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff new Object[] {((List) result.get(QueryConstants.QUERY_RESULT)).size(),
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff result.get(QueryConstants.STATISTICS_QUERY_TIME),
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff result.get(QueryConstants.STATISTICS_CONVERSION_TIME)});
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle return results;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } finally {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (db != null) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff db.close();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @return A connection from the pool. Call close on the connection when done to return to the pool.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws org.forgerock.openidm.objset.InternalServerErrorException
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODatabaseDocumentTx getConnection() throws InternalServerErrorException {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODatabaseDocumentTx db = null;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff int maxRetry = 100; // give it up to approx 10 seconds to recover
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff int retryCount = 0;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff while (db == null && retryCount < maxRetry) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff retryCount++;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff db = pool.acquire(dbURL, user, password);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (retryCount > 1) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.info("Succeeded in acquiring connection from pool in retry attempt {}", retryCount);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff retryCount = maxRetry;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (com.orientechnologies.orient.core.exception.ORecordNotFoundException ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // TODO: remove work-around once OrientDB resolves this condition
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (retryCount == maxRetry) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.warn("Failure reported acquiring connection from pool, retried {} times before giving up.", retryCount, ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw new InternalServerErrorException(
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff "Failure reported acquiring connection from pool, retried " + retryCount + " times before giving up: "
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff + ex.getMessage(), ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } else {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.info("Pool acquire reported failure, retrying - attempt {}", retryCount);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.trace("Pool acquire failure detail ", ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff Thread.sleep(100); // Give the DB time to complete what it's doing before retrying
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (InterruptedException iex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // ignore that sleep was interrupted
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff return db;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public static String typeToOrientClassName(String type) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff return type.replace("/", "_");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //public static String idToOrientClassName(String id) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // String type = getObjectType(id);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // return typeToOrientClassName(type);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff //}
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Detect if the root cause of the exception is an index constraint violation
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * This is necessary as the database may wrap this root cause in further exceptions,
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * masking the underlying cause
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param ex The throwable to check
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param maxLevels the maximum level of causes to check, avoiding the cost
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * of checking recursiveness
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @return
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff private boolean isCauseIndexException(Throwable ex, int maxLevels) {
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff return isCauseException (ex, OIndexException.class, maxLevels);
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff }
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff /**
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * Detect if the root cause of the exception is an index constraint violation
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * This is necessary as the database may wrap this root cause in further exceptions,
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * masking the underlying cause
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * @param ex The throwable to check
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * @param maxLevels the maximum level of causes to check, avoiding the cost
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * of checking recursiveness
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * @return
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff */
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff private boolean isCauseConcurrentModificationException(Throwable ex, int maxLevels) {
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff return isCauseException (ex, OConcurrentModificationException.class, maxLevels);
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff }
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff /**
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * Detect if the root cause of the exception is a specific OrientDB exception
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * This is necessary as the database may wrap this root cause in further exceptions,
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * masking the underlying cause
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * @param ex The throwable to check
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * @param clazz the specific OrientDB exception to check for
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * @param maxLevels the maximum level of causes to check, avoiding the cost
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * of checking recursiveness
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff * @return whether the root cause is the specified exception
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff */
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff private boolean isCauseException(Throwable ex, Class clazz, int maxLevels) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (maxLevels > 0) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff Throwable cause = ex.getCause();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (cause != null) {
7a2aa1b288f80a14f55d273370840a2a9c0aab85Andi Egloff return clazz.isInstance(cause) || isCauseException(cause, clazz, maxLevels - 1);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff return false;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Populate and return a repository service that knows how to query and manipulate configuration.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param repoConfig the bootstrap configuration
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @return the boot repository service. This instance is not managed by SCR and needs to be manually registered.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff static OrientDBRepoService getRepoBootService(Map repoConfig) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff OrientDBRepoService bootRepo = new OrientDBRepoService();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff JsonValue cfg = new JsonValue(repoConfig);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff bootRepo.init(cfg);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff return bootRepo;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Activate
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff void activate(ComponentContext compContext) throws Exception {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.debug("Activating Service with configuration {}", compContext.getProperties());
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff existingConfig = enhancedConfig.getConfigurationAsJson(compContext);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (RuntimeException ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.warn("Configuration invalid and could not be parsed, can not start OrientDB repository: "
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff + ex.getMessage(), ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw ex;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff embeddedServer = new EmbeddedOServerService();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff embeddedServer.activate(existingConfig);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff init(existingConfig);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.info("Repository started.");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Initialize the instnace with the given configuration.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * This can configure managed (DS/SCR) instances, as well as explicitly instantiated
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * (bootstrap) instances.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param config the configuration
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff void init (JsonValue config) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff dbURL = getDBUrl(config);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.info("Use DB at dbURL: {}", dbURL);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff user = getUser(config);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff password = getPassword(config);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff Map map = config.get(CONFIG_QUERIES).asMap();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff Map<String, String> queryMap = (Map<String, String>) map;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff queries.setConfiguredQueries(queryMap);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (RuntimeException ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.warn("Configuration invalid, can not start OrientDB repository", ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw ex;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff pool = DBHelper.getPool(dbURL, user, password, poolMinSize, poolMaxSize, config, true);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.debug("Obtained pool {}", pool);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (RuntimeException ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.warn("Initializing database pool failed", ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw ex;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff private String getDBUrl(JsonValue config) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff File dbFolder = IdentityServer.getFileForWorkingPath("db/openidm");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff String orientDbFolder = dbFolder.getAbsolutePath();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff orientDbFolder = orientDbFolder.replace('\\', '/'); // OrientDB does not handle backslashes well
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff return config.get(OrientDBRepoService.CONFIG_DB_URL).defaultTo("local:" + orientDbFolder).asString();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff private String getUser(JsonValue config) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff return config.get(CONFIG_USER).defaultTo("admin").asString();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff private String getPassword(JsonValue config) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff return config.get(CONFIG_PASSWORD).defaultTo("admin").asString();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Adapts a {@code Throwable} to a {@code ResourceException}. If the
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * {@code Throwable} is an JSON {@code JsonValueException} then an
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * appropriate {@code ResourceException} is returned, otherwise an
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * {@code InternalServerErrorException} is returned.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param t
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * The {@code Throwable} to be converted.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @return The equivalent resource exception.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff public ResourceException adapt(final Throwable t) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff int resourceResultCode;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw t;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (OConcurrentModificationException ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff resourceResultCode = ResourceException.VERSION_MISMATCH;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (final ResourceException e) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff return e;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (final JsonValueException e) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff resourceResultCode = ResourceException.BAD_REQUEST;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (final Throwable tmp) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff resourceResultCode = ResourceException.INTERNAL_ERROR;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff return ResourceException.getException(resourceResultCode, t.getMessage(), t);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Handle an existing activated service getting changed;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * e.g. configuration changes or dependency changes
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @param compContext THe OSGI component context
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws Exception if handling the modified event failed
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Modified
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff void modified(ComponentContext compContext) throws Exception {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.debug("Handle repository service modified notification");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff JsonValue newConfig = null;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff newConfig = enhancedConfig.getConfigurationAsJson(compContext);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } catch (RuntimeException ex) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.warn("Configuration invalid and could not be parsed, can not start OrientDB repository", ex);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff throw ex;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (existingConfig != null
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff && dbURL.equals(getDBUrl(newConfig))
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff && user.equals(getUser(newConfig))
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff && password.equals(getPassword(newConfig))) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // If the DB pool settings don't change keep the existing pool
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.info("(Re-)initialize repository with latest configuration.");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff init(newConfig);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff } else {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // If the DB pool settings changed do a more complete re-initialization
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.info("Re-initialize repository with latest configuration - including DB pool setting changes.");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff deactivate(compContext);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff activate(compContext);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff existingConfig = newConfig;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.debug("Repository service modified");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Deactivate
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff void deactivate(ComponentContext compContext) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.debug("Deactivating Service {}", compContext);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff cleanup();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (embeddedServer != null) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff embeddedServer.deactivate();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.info("Repository stopped.");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Cleanup and close the repository
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff */
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff void cleanup() {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff DBHelper.closePools();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff}