5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff/*
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff *
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener * Copyright 2011-2015 ForgeRock AS
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
7b16651ac38dde7780c1272067b1938e275325acJim Mitchenerimport static org.forgerock.json.resource.CountPolicy.EXACT;
7b16651ac38dde7780c1272067b1938e275325acJim Mitchenerimport static org.forgerock.json.resource.CountPolicy.NONE;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport static org.forgerock.json.resource.QueryResponse.NO_COUNT;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport static org.forgerock.json.resource.Responses.newActionResponse;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport static org.forgerock.json.resource.Responses.newQueryResponse;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport static org.forgerock.json.resource.Responses.newResourceResponse;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener
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
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchenerimport org.apache.commons.lang3.StringUtils;
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;
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienleimport org.apache.felix.scr.annotations.Reference;
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienleimport org.apache.felix.scr.annotations.ReferencePolicy;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.apache.felix.scr.annotations.Service;
b84068e6021ec8a830c26c4494f6e335d1f9c0efBrendan Millerimport org.forgerock.openidm.router.IDMConnectionFactory;
21dcdac963f79c098a5ea1a2c5c5e109429c9786Brendan Millerimport org.forgerock.services.context.Context;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport org.forgerock.json.JsonValue;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport org.forgerock.json.JsonValueException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ActionRequest;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport org.forgerock.json.resource.ActionResponse;
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;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport org.forgerock.json.resource.QueryResourceHandler;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport org.forgerock.json.resource.QueryResponse;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ReadRequest;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.RequestHandler;
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienleimport org.forgerock.json.resource.Requests;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport org.forgerock.json.resource.ResourceResponse;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.ResourceException;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloffimport org.forgerock.json.resource.UpdateRequest;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.config.enhanced.EnhancedConfig;
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;
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmillerimport org.forgerock.openidm.repo.orientdb.impl.query.Commands;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.repo.orientdb.impl.query.PredefinedQueries;
2c688737c6d30095ecf42f0ad82748deb01939feChad Kienleimport org.forgerock.openidm.repo.orientdb.impl.query.Queries;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchenerimport org.forgerock.util.promise.Promise;
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;
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drakeimport com.orientechnologies.orient.core.storage.ORecordDuplicatedException;
812374fe11d8d95123cb0fd35447c43385a90549Chris Drakeimport com.orientechnologies.orient.core.version.OSimpleVersion;
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff/**
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * Repository service implementation using OrientDB
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";
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller public static final String CONFIG_COMMANDS = "commands";
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";
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener public static final String CONFIG_POOL_MIN_SIZE = "poolMinSize";
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener public static final String CONFIG_POOL_MAX_SIZE = "poolMaxSize";
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";
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller private enum Action {
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller updateDbCredentials,
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller command
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller }
7a0b4bab2de703c8967aa0fea836ded4b9805112Brendan Mmiller
7a0b4bab2de703c8967aa0fea836ded4b9805112Brendan Mmiller /** The Connection Factory */
b84068e6021ec8a830c26c4494f6e335d1f9c0efBrendan Miller @Reference(policy = ReferencePolicy.STATIC)
b84068e6021ec8a830c26c4494f6e335d1f9c0efBrendan Miller protected IDMConnectionFactory connectionFactory;
7a0b4bab2de703c8967aa0fea836ded4b9805112Brendan Mmiller
2312a9d8e5342846988edcd629e1ac56a8b5d4a9Brendan Mmiller /** Enhanced configuration service. */
2312a9d8e5342846988edcd629e1ac56a8b5d4a9Brendan Mmiller @Reference(policy = ReferencePolicy.DYNAMIC)
2312a9d8e5342846988edcd629e1ac56a8b5d4a9Brendan Mmiller private EnhancedConfig enhancedConfig;
2312a9d8e5342846988edcd629e1ac56a8b5d4a9Brendan Mmiller
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener private ODatabaseDocumentPool pool;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener private static final int DEFAULT_POOL_MIN_SIZE = 5;
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener private static final int DEFAULT_POOL_MAX_SIZE = 20;
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener private String dbURL;
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener private String user;
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener private String password;
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener private int poolMinSize;
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener private int poolMaxSize;
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle // Used to synchronize operations on the DB that require user/password credentials
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle private static Object dbLock = new Object();
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle private static OrientDBRepoService bootRepo = null;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // Current configuration
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff JsonValue existingConfig;
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff // TODO: evaluate use of Guice instead
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff PredefinedQueries predefinedQueries = new PredefinedQueries();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff Queries queries = new Queries();
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller Commands commands = new Commands();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff EmbeddedOServerService embeddedServer;
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public Promise<ResourceResponse, ResourceException> handleRead(final Context context, final ReadRequest request) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
623d32cac07d4848161965256739521f868f54b5Brendan Miller return read(request).asPromise();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener } catch (Exception ex) {
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return adapt(ex).asPromise();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public Promise<ResourceResponse, ResourceException> handleCreate(final Context context, final CreateRequest request) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
623d32cac07d4848161965256739521f868f54b5Brendan Miller return create(request).asPromise();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener } catch (Exception ex) {
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return adapt(ex).asPromise();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public Promise<ResourceResponse, ResourceException> handleUpdate(final Context context, UpdateRequest request) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
623d32cac07d4848161965256739521f868f54b5Brendan Miller return update(request).asPromise();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener } catch (Exception ex) {
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return adapt(ex).asPromise();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public Promise<ResourceResponse, ResourceException> handleDelete(final Context context, final DeleteRequest request) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
623d32cac07d4848161965256739521f868f54b5Brendan Miller return delete(request).asPromise();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener } catch (Exception ex) {
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return adapt(ex).asPromise();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener
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 */
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public ResourceResponse read(ReadRequest request) throws ResourceException {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener if (request.getResourcePathObject().size() < 2) {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener throw new NotFoundException("The object identifier did not include sufficient information to determine the object type and identifier of the object to read: " + request.getResourcePath());
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final String type = request.getResourcePathObject().parent().toString();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final String localId = request.getResourcePathObject().leaf();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener ResourceResponse 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);
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener logger.trace("Completed get for id: {} result: {}", request.getResourcePath(), 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 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 */
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public ResourceResponse create(CreateRequest request) throws ResourceException {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener if (request.getResourcePathObject().isEmpty()) {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener throw new NotFoundException("The object identifier did not include sufficient information to determine the object type: " + request.getResourcePath());
59e1ef1c5c1b51a036ace7fc1da0d3e2da6ef0b0Andi Egloff }
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final String type = request.getResourcePath();
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
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener String fullId = request.getResourcePathObject().child(localId).toString();
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);
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener return newResourceResponse(obj.get(DocumentUtil.TAG_ID).asString(), obj.get(DocumentUtil.TAG_REV).asString(), obj);
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake } catch (ORecordDuplicatedException ex) {
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake // Because the OpenIDM ID is defined as unique, duplicate inserts must fail
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake throw new PreconditionFailedException("Create rejected as Object with same ID already exists. " + ex.getMessage(), ex);
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);
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake } catch (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.
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake if (isCauseIndexException(ex, 10) || isCauseRecordDuplicatedException(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 }
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff /**
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener * 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 *
280f5f217c81e0b90c2b526a8a03849c1371545cBrendan Mmiller * @param request the contents of the object to update
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ConflictException if version is required but is {@code null}.
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff * @throws ForbiddenException if access to the object is forbidden.
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener * @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 */
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public ResourceResponse update(UpdateRequest request) throws ResourceException {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener if (request.getResourcePathObject().size() < 2) {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener throw new NotFoundException("The object identifier did not include sufficient information to determine the object type and identifier of the object to update: " + request.getResourcePath());
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller }
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final String type = request.getResourcePathObject().parent().toString();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final String localId = request.getResourcePathObject().leaf();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff String orientClassName = typeToOrientClassName(type);
1efcf1f855dad06a6ee70d96ffa9ea8145e6fc9bBrendan Mmiller JsonValue obj = request.getContent();
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) {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener throw new NotFoundException("Update on object " + request.getResourcePath() + " could not find existing object.");
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff ODocument updatedDoc = DocumentUtil.toDocument(obj, existingDoc, db, orientClassName);
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener logger.trace("Updated doc for id {} to save {}", request.getResourcePath(), 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));
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener logger.debug("Committed update for id: {} revision: {}", request.getResourcePath(), updatedDoc.getVersion());
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener logger.trace("Update payload for id: {} doc: {}", request.getResourcePath(), updatedDoc);
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener return newResourceResponse(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 */
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public ResourceResponse delete(DeleteRequest request) throws ResourceException {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener if (request.getResourcePathObject().size() < 2) {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener throw new NotFoundException("The object identifier did not include sufficient information to determine the object type and identifier of the object to update: " + request.getResourcePath());
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
0f8068490e3337fd4abce8b44b26c917bdc3bc8eJim Mitchener if (request.getRevision() == null || "".equals(request.getRevision())) {
0f8068490e3337fd4abce8b44b26c917bdc3bc8eJim Mitchener throw new ConflictException("Object passed into delete does not have revision it expects set.");
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller }
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final String type = request.getResourcePathObject().parent().toString();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final String localId = request.getResourcePathObject().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) {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener throw new NotFoundException("Object does not exist for delete on: " + request.getResourcePath());
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
812374fe11d8d95123cb0fd35447c43385a90549Chris Drake db.delete(existingDoc.getIdentity(), new OSimpleVersion(ver));
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller logger.debug("delete for id succeeded: {} revision: {}", localId, request.getRevision());
a895d40dfd8a6e16506c1874d8740e2d00bee0b4Chad Kienle return DocumentUtil.toResource(existingDoc);
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
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public Promise<ResourceResponse, ResourceException> handlePatch(final Context context, final PatchRequest request) {
3a5174589eea431f6da6dc1bc3f20e3d358377f8Andi Egloff // TODO: impl
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return adapt(new NotSupportedException("Patch not supported yet")).asPromise();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public Promise<ActionResponse, ResourceException> handleAction(final Context context, final ActionRequest request) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle try {
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller Map<String, String> params = request.getAdditionalParameters();
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller switch (request.getActionAsEnum(Action.class)) {
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller case updateDbCredentials:
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller String newUser = params.get("user");
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller String newPassword = params.get("password");
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller if (newUser == null || newPassword == null) {
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return adapt(new BadRequestException("Expecting 'user' and 'password' parameters")).asPromise();
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller }
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller synchronized (dbLock) {
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller DBHelper.updateDbCredentials(dbURL, user, password, newUser, newPassword);
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller JsonValue config = connectionFactory.getConnection().read(context, Requests.newReadRequest("config", PID)).getContent();
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller config.put("user", newUser);
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller config.put("password", newPassword);
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller UpdateRequest updateRequest = Requests.newUpdateRequest("config/" + PID, config);
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller connectionFactory.getConnection().update(context, updateRequest);
623d32cac07d4848161965256739521f868f54b5Brendan Miller return newActionResponse(new JsonValue(params)).asPromise();
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller }
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller case command:
623d32cac07d4848161965256739521f868f54b5Brendan Miller return newActionResponse(new JsonValue(command(request))).asPromise();
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller default:
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return adapt(new BadRequestException("Unknown action: " + request.getAction())).asPromise();
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle }
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller } catch (IllegalArgumentException e) {
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return adapt(new BadRequestException("Unknown action: " + request.getAction())).asPromise();
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle } catch (ResourceException e) {
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return e.asPromise();
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller /**
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller * Execute a database command according to the details in the action request.
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller *
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller * @param request the ActionRequest
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller * @return the number of affected rows/records.
280f5f217c81e0b90c2b526a8a03849c1371545cBrendan Mmiller * @throws ResourceException on failure to resolved query
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller */
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller public Object command(ActionRequest request) throws ResourceException {
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller ODatabaseDocumentTx db = getConnection();
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller try {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener return commands.query(request.getResourcePath(), request, db);
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller } finally {
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller if (db != null) {
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller db.close();
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller }
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller }
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller }
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller
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 */
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public Promise<QueryResponse, ResourceException> handleQuery(final Context context, final QueryRequest request,
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final QueryResourceHandler handler) {
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener // If paged results are requested then decode the cookie in order to determine
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener // the index of the first result to be returned.
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener final int requestPageSize = request.getPageSize();
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener // Cookie containing offset of last request
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener final String pagedResultsCookie = request.getPagedResultsCookie();
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener final boolean pagedResultsRequested = requestPageSize > 0;
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener // index of first record (used for SKIP/OFFSET)
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener final int firstResultIndex;
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener if (pagedResultsRequested) {
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener if (StringUtils.isNotEmpty(pagedResultsCookie)) {
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener try {
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener firstResultIndex = Integer.parseInt(pagedResultsCookie);
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener } catch (final NumberFormatException e) {
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return new BadRequestException("Invalid paged results cookie").asPromise();
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener }
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener } else {
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener firstResultIndex = Math.max(0, request.getPagedResultsOffset());
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener }
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener } else {
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener firstResultIndex = 0;
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener }
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener // Once cookie is processed Queries.query() can rely on the offset.
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener request.setPagedResultsOffset(firstResultIndex);
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener try {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener List<ResourceResponse> results = query(request);
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener for (ResourceResponse result : results) {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener handler.handleResource(result);
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener }
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener /*
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener * Execute additional -count query if we are paging
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener */
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final String nextCookie;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle // The number of results (if known)
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final int resultCount;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener if (pagedResultsRequested) {
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener // count if requested
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener switch (request.getTotalPagedResultsPolicy()) {
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener case ESTIMATE:
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener case EXACT:
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener // Get total if -count query is available
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener final String countQueryId = request.getQueryId() + "-count";
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener if (queries.queryIdExists(countQueryId)) {
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener QueryRequest countRequest = Requests.copyOfQueryRequest(request);
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener countRequest.setQueryId(countQueryId);
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener // Strip pagination parameters
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener countRequest.setPageSize(0);
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener countRequest.setPagedResultsOffset(0);
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener countRequest.setPagedResultsCookie(null);
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener List<ResourceResponse> countResult = query(countRequest);
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener if (countResult != null && !countResult.isEmpty()) {
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener resultCount = countResult.get(0).getContent().get("total").asInteger();
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener } else {
d21f2a0e3f1ab2bb35ddb5f39936522c429fdd4cBrendan Miller logger.debug("Count query {} failed.", countQueryId);
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener resultCount = NO_COUNT;
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener }
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener } else {
d21f2a0e3f1ab2bb35ddb5f39936522c429fdd4cBrendan Miller logger.debug("No count query found with id {}", countQueryId);
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener resultCount = NO_COUNT;
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener }
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener break;
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener case NONE:
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener default:
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener resultCount = NO_COUNT;
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener break;
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener }
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener if (results.size() < requestPageSize) {
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle nextCookie = null;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle } else {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener final int remainingResults = resultCount - (firstResultIndex + results.size());
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener if (remainingResults == 0) {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener nextCookie = null;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener } else {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener nextCookie = String.valueOf(firstResultIndex + requestPageSize);
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener }
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle }
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener } else {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener resultCount = NO_COUNT;
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener nextCookie = null;
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener }
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener if (resultCount == NO_COUNT) {
623d32cac07d4848161965256739521f868f54b5Brendan Miller return newQueryResponse(nextCookie).asPromise();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener } else {
7b16651ac38dde7780c1272067b1938e275325acJim Mitchener return newQueryResponse(nextCookie, EXACT, resultCount).asPromise();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener }
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener } catch (ResourceException e) {
ed1b8fdb2169651136922938ef778b05dae17d5dBrendanMiller return e.asPromise();
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener }
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
bdcef189d83ab13d06dde11499a03b16cdb5b432Chad Kienle }
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener @Override
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener public List<ResourceResponse> query(final QueryRequest request) throws ResourceException {
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener List<ResourceResponse> results = new ArrayList<ResourceResponse>();
39a2b1613dd6d8f45a32a9efda82fd0aead4cf43Brendan Mmiller
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener logger.trace("Full id: {} Extracted type: {}", request.getResourcePath(), request.getResourcePath());
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();
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener List<ODocument> queryResult = queries.query(request.getResourcePath(), 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);
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener results.add(newResourceResponse(
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener (String) convertedEntry.get(DocumentUtil.TAG_ID),
f242d1a2135048008020f65a531cd5a811170e4bJim Mitchener (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.",
7a1210ba566121560af463fb7b058908168d0352Chad Kienle new Object[] {results.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.
280f5f217c81e0b90c2b526a8a03849c1371545cBrendan Mmiller * @throws 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
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle synchronized (dbLock) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle while (db == null && retryCount < maxRetry) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle retryCount++;
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle try {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle db = pool.acquire(dbURL, user, password);
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle if (retryCount > 1) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle logger.info("Succeeded in acquiring connection from pool in retry attempt {}", retryCount);
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle }
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle retryCount = maxRetry;
49f22453ad3bcb0b3f0b50c405e3f5fb22ed0282Chris Drake } catch (com.orientechnologies.common.concur.lock.OLockException ex) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle // TODO: remove work-around once OrientDB resolves this condition
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle if (retryCount == maxRetry) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle logger.warn("Failure reported acquiring connection from pool, retried {} times before giving up.", retryCount, ex);
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle throw new InternalServerErrorException(
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle "Failure reported acquiring connection from pool, retried " + retryCount + " times before giving up: "
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle + ex.getMessage(), ex);
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle } else {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle logger.info("Pool acquire reported failure, retrying - attempt {}", retryCount);
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle logger.trace("Pool acquire failure detail ", ex);
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle try {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle Thread.sleep(100); // Give the DB time to complete what it's doing before retrying
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle } catch (InterruptedException iex) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle // ignore that sleep was interrupted
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle }
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 //}
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake /**
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake * Detect if the root cause of the exception is a duplicate record.
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake * This is necessary as the database may wrap this root cause in further exceptions,
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake * masking the underlying cause
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake * @param ex The throwable to check
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake * @param maxLevels the maximum level of causes to check, avoiding the cost
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake * of checking recursiveness
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake * @return
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake */
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake private boolean isCauseRecordDuplicatedException(Throwable ex, int maxLevels) {
a1d206a2a22b5cde9b00633ea4472ae0b144d695Brendan Mmiller return isCauseException (ex, ORecordDuplicatedException.class, maxLevels);
cbb34fee4a2f478defcf65ea232dd810b46c0f0bChris Drake }
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 */
399a0a33fc502af77b2e74bb087bfa51b81422c0Brendan Mmiller 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 */
399a0a33fc502af77b2e74bb087bfa51b81422c0Brendan Mmiller static OrientDBRepoService getRepoBootService(Map<String, Object> repoConfig) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle if (bootRepo == null) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle bootRepo = new OrientDBRepoService();
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle }
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 /**
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener * Initialize the instance 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) {
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle synchronized (dbLock) {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff try {
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff dbURL = getDBUrl(config);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff logger.info("Use DB at dbURL: {}", dbURL);
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener user = config.get(CONFIG_USER).defaultTo("admin").asString();
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener password = config.get(CONFIG_PASSWORD).defaultTo("admin").asString();
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener poolMinSize = config.get(CONFIG_POOL_MIN_SIZE).defaultTo(DEFAULT_POOL_MIN_SIZE).asInteger();
10fed32259a7430282ad1db9194450808ad5d932Jim Mitchener poolMaxSize = config.get(CONFIG_POOL_MAX_SIZE).defaultTo(DEFAULT_POOL_MAX_SIZE).asInteger();
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller Map<String, String> queryMap = config.get(CONFIG_QUERIES)
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller .defaultTo(new HashMap<String, String>())
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller .asMap(String.class);
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff queries.setConfiguredQueries(queryMap);
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller Map<String, String> commandMap = config.get(CONFIG_COMMANDS)
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller .defaultTo(new HashMap<String, String>())
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller .asMap(String.class);
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller commands.setConfiguredQueries(commandMap);
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 }
08b028f3cb5252a892956716ce12f4770e69f423Chad Kienle }
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
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake private String getUser(JsonValue config) {
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake return config.get(CONFIG_USER).defaultTo("admin").asString();
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake }
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake private String getPassword(JsonValue config) {
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake return config.get(CONFIG_PASSWORD).defaultTo("admin").asString();
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake }
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake private int getPoolMinSize(JsonValue config) {
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake return config.get(CONFIG_POOL_MIN_SIZE).defaultTo(DEFAULT_POOL_MIN_SIZE).asInteger();
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake }
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake private int getPoolMaxSize(JsonValue config) {
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake return config.get(CONFIG_POOL_MAX_SIZE).defaultTo(DEFAULT_POOL_MAX_SIZE).asInteger();
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake }
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake
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 }
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake if (existingConfig != null
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake && !existingConfig.get("embeddedServer").equals(newConfig.get("embeddedServer"))) {
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake // The embedded server configuration has changed so re-initialize it.
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake embeddedServer.modified(newConfig);
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake }
5c625c17767aae6167c744e9e1ad40c3d1455e31Andi Egloff if (existingConfig != null
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake && user.equals(getUser(newConfig))
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake && password.equals(getPassword(newConfig))
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake && dbURL.equals(getDBUrl(newConfig))
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake && poolMinSize == getPoolMinSize(newConfig)
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake && poolMaxSize == getPoolMaxSize(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 } 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.");
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake DBHelper.closePool(dbURL, pool);
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake }
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake init(newConfig);
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake if (bootRepo != null) {
dcb5656be9fc7c325b148ad479fb959c0eadc414Chris Drake bootRepo.init(newConfig);
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}