37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle/*
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * Copyright © 2011-2015 ForgeRock AS. All rights reserved.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * The contents of this file are subject to the terms
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * of the Common Development and Distribution License
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * (the License). You may not use this file except in
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * compliance with the License.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * You can obtain a copy of the License at
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * http://forgerock.org/license/CDDLv1.0.html
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * See the License for the specific language governing
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * permission and limitations under the License.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * When distributing Covered Code, include this CDDL
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Header Notice in each file and include the License file
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * at http://forgerock.org/license/CDDLv1.0.html
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * If applicable, add the following below the CDDL Header,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * with the fields enclosed by brackets [] replaced by
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * your own identifying information:
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * "Portions Copyrighted [year] [name of copyright owner]"
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37c999796383567df3db1f9d2a09942d83d413caChad Kienlepackage org.forgerock.openidm.repo.jdbc.impl;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchenerimport static org.forgerock.json.resource.Responses.newResourceResponse;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienleimport static org.forgerock.openidm.repo.QueryConstants.PAGED_RESULTS_OFFSET;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienleimport static org.forgerock.openidm.repo.QueryConstants.PAGE_SIZE;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienleimport static org.forgerock.openidm.repo.QueryConstants.SORT_KEYS;
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmillerimport static org.forgerock.openidm.repo.util.Clauses.where;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.io.IOException;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.sql.Connection;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.sql.PreparedStatement;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.sql.ResultSet;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.sql.ResultSetMetaData;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.sql.SQLException;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.sql.Statement;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.util.ArrayList;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.util.Arrays;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.util.Collections;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.util.EnumMap;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.util.HashMap;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.util.LinkedHashMap;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.util.List;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport java.util.Map;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchenerimport com.fasterxml.jackson.core.type.TypeReference;
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchenerimport com.fasterxml.jackson.databind.ObjectMapper;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienleimport org.apache.commons.lang3.StringUtils;
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchenerimport org.forgerock.json.JsonPointer;
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchenerimport org.forgerock.json.JsonValue;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.json.resource.InternalServerErrorException;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.json.resource.NotFoundException;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.json.resource.PreconditionFailedException;
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchenerimport org.forgerock.json.resource.ResourceResponse;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.json.resource.ResourceException;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienleimport org.forgerock.json.resource.SortKey;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.openidm.repo.jdbc.ErrorType;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.openidm.repo.jdbc.SQLExceptionHandler;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.forgerock.openidm.repo.jdbc.TableHandler;
37c999796383567df3db1f9d2a09942d83d413caChad Kienleimport org.forgerock.openidm.repo.jdbc.impl.query.QueryResultMapper;
37c999796383567df3db1f9d2a09942d83d413caChad Kienleimport org.forgerock.openidm.repo.jdbc.impl.query.TableQueries;
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchenerimport org.forgerock.util.query.QueryFilter;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.slf4j.Logger;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleimport org.slf4j.LoggerFactory;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle/**
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Handling of tables in a generic (not object specific) layout
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienlepublic class GenericTableHandler implements TableHandler {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final static Logger logger = LoggerFactory.getLogger(GenericTableHandler.class);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
505a315052c2c6b495cb3228847d7d33d17458d1Jim Mitchener /**
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener * Maximum length of searchable properties.
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener * This is used to trim values due to database index size limitations.
505a315052c2c6b495cb3228847d7d33d17458d1Jim Mitchener */
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller protected static final int SEARCHABLE_LENGTH = 2000;
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle SQLExceptionHandler sqlExceptionHandler;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle GenericTableConfig cfg;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final String mainTableName;
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener final String propTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final String dbSchemaName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Jackson parser
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final ObjectMapper mapper = new ObjectMapper();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Type information for the Jackson parser
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final TypeReference<LinkedHashMap<String,Object>> typeRef = new TypeReference<LinkedHashMap<String,Object>>() {};
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final TableQueries queries;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Map<QueryDefinition, String> queryMap;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final boolean enableBatching; // Whether to use JDBC statement batching.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int maxBatchSize; // The maximum number of statements to batch together. If max batch size is 1, do not use batching.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public enum QueryDefinition {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle READTYPEQUERYSTR,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CREATETYPEQUERYSTR,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle READFORUPDATEQUERYSTR,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle READQUERYSTR,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CREATEQUERYSTR,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle UPDATEQUERYSTR,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle DELETEQUERYSTR,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PROPCREATEQUERYSTR,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PROPDELETEQUERYSTR,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle QUERYALLIDS
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener @Override
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener public boolean queryIdExists(String queryId) {
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener return queries.queryIdExists(queryId);
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener }
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller /**
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * Create a generic table handler using a QueryFilterVisitor that uses generic object property tables to process
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * query filters.
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller *
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param tableConfig the table config
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param dbSchemaName the schem name
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param queriesConfig a map of named queries
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param commandsConfig a map of named commands
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param maxBatchSize the maximum batch size
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller * @param sqlExceptionHandler a handler for SQLExceptions
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller */
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller public GenericTableHandler(JsonValue tableConfig,
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller String dbSchemaName,
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller JsonValue queriesConfig,
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller JsonValue commandsConfig,
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller int maxBatchSize,
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller SQLExceptionHandler sqlExceptionHandler) {
a17f398e6526e91a7a3df8dbe0e7f8dd1e8e581dBrendan Mmiller
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle cfg = GenericTableConfig.parse(tableConfig);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle this.mainTableName = cfg.mainTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle this.propTableName = cfg.propertiesTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle this.dbSchemaName = dbSchemaName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (maxBatchSize < 1) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle this.maxBatchSize = 1;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle this.maxBatchSize = maxBatchSize;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (sqlExceptionHandler == null) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle this.sqlExceptionHandler = new DefaultSQLExceptionHandler();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle this.sqlExceptionHandler = sqlExceptionHandler;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller queries = new TableQueries(this, mainTableName, propTableName, dbSchemaName, getSearchableLength(), new GenericQueryResultMapper());
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle queryMap = Collections.unmodifiableMap(initializeQueryMap());
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller queries.setConfiguredQueries(queriesConfig, commandsConfig, queryMap);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // TODO: Consider taking into account DB meta-data rather than just configuration
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle //DatabaseMetaData metadata = connection.getMetaData();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle //boolean isBatchingSupported = metadata.supportsBatchUpdates();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle //if (!isBatchingSupported) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // maxBatchSize = 1;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle //}
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle enableBatching = (this.maxBatchSize > 1);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (enableBatching) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.info("JDBC statement batching enabled, maximum batch size {}", this.maxBatchSize);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.info("JDBC statement batching disabled.");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener /**
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener * Get the length of the searchable index.
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener */
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener int getSearchableLength() {
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener return SEARCHABLE_LENGTH;
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener }
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle protected Map<QueryDefinition, String> initializeQueryMap() {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Map<QueryDefinition, String> result = new EnumMap<QueryDefinition, String>(QueryDefinition.class);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String typeTable = dbSchemaName == null ? "objecttypes" : dbSchemaName + ".objecttypes";
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String mainTable = dbSchemaName == null ? mainTableName : dbSchemaName + "." + mainTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String propertyTable = dbSchemaName == null ? propTableName : dbSchemaName + "." + propTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // objecttypes table
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.CREATETYPEQUERYSTR, "INSERT INTO " + typeTable + " (objecttype) VALUES (?)");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.READTYPEQUERYSTR, "SELECT id FROM " + typeTable + " objtype WHERE objtype.objecttype = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Main object table
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.READFORUPDATEQUERYSTR, "SELECT obj.* FROM " + mainTable + " obj INNER JOIN " + typeTable + " objtype ON obj.objecttypes_id = objtype.id AND objtype.objecttype = ? WHERE obj.objectid = ? FOR UPDATE");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.READQUERYSTR, "SELECT obj.rev, obj.fullobject FROM " + typeTable + " objtype, " + mainTable + " obj WHERE obj.objecttypes_id = objtype.id AND objtype.objecttype = ? AND obj.objectid = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.CREATEQUERYSTR, "INSERT INTO " + mainTable + " (objecttypes_id, objectid, rev, fullobject) VALUES (?,?,?,?)");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.UPDATEQUERYSTR, "UPDATE " + mainTable + " obj SET obj.objectid = ?, obj.rev = ?, obj.fullobject = ? WHERE obj.id = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.DELETEQUERYSTR, "DELETE obj FROM " + mainTable + " obj INNER JOIN " + typeTable + " objtype ON obj.objecttypes_id = objtype.id AND objtype.objecttype = ? WHERE obj.objectid = ? AND obj.rev = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* DB2 Script
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deleteQueryStr = "DELETE FROM " + dbSchemaName + "." + mainTableName + " obj WHERE EXISTS (SELECT 1 FROM " + dbSchemaName + ".objecttypes objtype WHERE obj.objecttypes_id = objtype.id AND objtype.objecttype = ?) AND obj.objectid = ? AND obj.rev = ?";
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Object properties table
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.PROPCREATEQUERYSTR, "INSERT INTO " + propertyTable + " ( " + mainTableName + "_id, propkey, proptype, propvalue) VALUES (?,?,?,?)");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.PROPDELETEQUERYSTR, "DELETE prop FROM " + propertyTable + " prop INNER JOIN " + mainTable + " obj ON prop." + mainTableName + "_id = obj.id INNER JOIN " + typeTable + " objtype ON obj.objecttypes_id = objtype.id WHERE objtype.objecttype = ? AND obj.objectid = ?");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Default object queries
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String tableVariable = dbSchemaName == null ? "${_mainTable}" : "${_dbSchema}.${_mainTable}";
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.put(QueryDefinition.QUERYALLIDS, "SELECT obj.objectid FROM " + tableVariable + " obj INNER JOIN " + typeTable + " objtype ON obj.objecttypes_id = objtype.id WHERE objtype.objecttype = ${_resource}");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return result;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* (non-Javadoc)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @see org.forgerock.openidm.repo.jdbc.impl.TableHandler#read(java.lang.String, java.lang.String, java.lang.String, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle @Override
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchener public ResourceResponse read(String fullId, String type, String localId, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws ResourceException, SQLException, IOException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchener ResourceResponse result = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Map<String, Object> resultMap = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement readStatement = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ResultSet rs = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readStatement = getPreparedStatement(connection, QueryDefinition.READQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating prepared statement {} for {}", readStatement, fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readStatement.setString(1, type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readStatement.setString(2, localId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", readStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle rs = readStatement.executeQuery();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (rs.next()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String rev = rs.getString("rev");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String objString = rs.getString("fullobject");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle resultMap = mapper.readValue(objString, typeRef);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle resultMap.put("_rev", rev);
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller logger.debug(" full id: {}, rev: {}, obj {}", fullId, rev, resultMap);
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchener return newResourceResponse(localId, rev, new JsonValue(resultMap));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw ResourceException.getException(ResourceException.NOT_FOUND,
a1d206a2a22b5cde9b00633ea4472ae0b144d695Brendan Mmiller "Object " + fullId + " not found in " + type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } finally {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(rs);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(readStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* (non-Javadoc)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @see org.forgerock.openidm.repo.jdbc.impl.TableHandler#create(java.lang.String, java.lang.String, java.lang.String, java.util.Map, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle @Override
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public void create(String fullId, String type, String localId, Map<String, Object> obj, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws SQLException, IOException, InternalServerErrorException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long typeId = getTypeId(type, connection); // Note this call can commit and start a new transaction in some cases
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement createStatement = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle createStatement = queries.getPreparedStatement(connection, queryMap.get(QueryDefinition.CREATEQUERYSTR), true);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Create with fullid {}", fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String rev = "0";
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_id", localId); // Save the id in the object
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_rev", rev); // Save the rev in the object, and return the changed rev from the create.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String objString = mapper.writeValueAsString(obj);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating statement {} with params {}, {}, {}, {}",
10ce1c22b1cd83482f0127254fe8a27610bb25f5Brendan Miller queryMap.get(QueryDefinition.CREATEQUERYSTR), typeId, localId, rev, objString);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle createStatement.setLong(1, typeId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle createStatement.setString(2, localId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle createStatement.setString(3, rev);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle createStatement.setString(4, objString);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", createStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int val = createStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ResultSet keys = createStatement.getGeneratedKeys();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean validKeyEntry = keys.next();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (!validKeyEntry) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new InternalServerErrorException("Object creation for " + fullId + " failed to retrieve an assigned ID from the DB.");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long dbId = keys.getLong(1);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Created object for id {} with rev {}", fullId, rev);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle JsonValue jv = new JsonValue(obj);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle writeValueProperties(fullId, dbId, localId, jv, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } finally {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(createStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /**
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Writes all properties of a given resource to the properties table and links them to the main table record.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param fullId the full URI of the resource the belongs to
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param dbId the generated identifier to link the properties table with the main table (foreign key)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param localId the local identifier of the resource these properties belong to
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param value the JSON value with the properties to write
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the DB connection
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws SQLException if the insert failed
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle void writeValueProperties(String fullId, long dbId, String localId, JsonValue value, Connection connection) throws SQLException {
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff if (cfg.hasPossibleSearchableProperties()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Integer batchingCount = 0;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement propCreateStatement = getPreparedStatement(connection, QueryDefinition.PROPCREATEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle batchingCount = writeValueProperties(fullId, dbId, localId, value, connection, propCreateStatement, batchingCount);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (enableBatching && batchingCount > 0) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int[] numUpdates = propCreateStatement.executeBatch();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Batch update of objectproperties updated: {}", numUpdates);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (logger.isDebugEnabled()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Writing batch of objectproperties, updated: {}", Arrays.asList(numUpdates));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle propCreateStatement.clearBatch();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } finally {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(propCreateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /**
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Internal recursive function to add/write properties.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * If batching is enabled, prepared statements are added to the batch and only executed if they hit the max limit.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * After completion returns the number of properties that have only been added to the batch but not yet executed.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * The caller is responsible for executing the batch on remaining items when it deems the batch complete.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * If batching is not enabled, prepared statements are immediately executed.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param fullId the full URI of the resource the belongs to
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param dbId the generated identifier to link the properties table with the main table (foreign key)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param localId the local identifier of the resource these properties belong to
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param value the JSON value with the properties to write
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the DB connection
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param propCreateStatement the prepared properties insert statement
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param batchingCount the current number of statements that have been batched and not yet executed on the prepared statement
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @return status of the current batchingCount, i.e. how many statements are not yet executed in the PreparedStatement
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws SQLException if the insert failed
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle private int writeValueProperties(String fullId, long dbId, String localId, JsonValue value, Connection connection,
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement propCreateStatement, int batchingCount) throws SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle for (JsonValue entry : value) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle JsonPointer propPointer = entry.getPointer();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (cfg.isSearchable(propPointer)) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String propkey = propPointer.toString();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (entry.isMap() || entry.isList()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle batchingCount = writeValueProperties(fullId, dbId, localId, entry, connection, propCreateStatement, batchingCount);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String propvalue = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Object val = entry.getObject();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (val != null) {
5848ca5e742e748dfd18fc53d810fa017949f809Jim Mitchener propvalue = StringUtils.left(val.toString(), getSearchableLength());
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String proptype = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (propvalue != null) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle proptype = entry.getObject().getClass().getName(); // TODO: proper type info
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (logger.isTraceEnabled()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating statement {} with params {}, {}, {}, {}, {}",
10ce1c22b1cd83482f0127254fe8a27610bb25f5Brendan Miller queryMap.get(QueryDefinition.PROPCREATEQUERYSTR), dbId, localId, propkey, proptype, propvalue);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle propCreateStatement.setLong(1, dbId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle propCreateStatement.setString(2, propkey);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle propCreateStatement.setString(3, proptype);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle propCreateStatement.setString(4, propvalue);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", propCreateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (enableBatching) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle propCreateStatement.addBatch();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle batchingCount++;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int numUpdate = propCreateStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (logger.isTraceEnabled()) {
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller logger.trace("Inserting objectproperty id: {} propkey: {} proptype: {}, propvalue: {}", fullId, propkey, proptype, propvalue);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (enableBatching && batchingCount >= maxBatchSize) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int[] numUpdates = propCreateStatement.executeBatch();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (logger.isDebugEnabled()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Batch limit reached, update of objectproperties updated: {}", Arrays.asList(numUpdates));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle propCreateStatement.clearBatch();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle batchingCount = 0;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return batchingCount;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /**
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @inheritDoc
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean isErrorType(SQLException ex, ErrorType errorType) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return sqlExceptionHandler.isErrorType(ex, errorType);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /**
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * @inheritDoc
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean isRetryable(SQLException ex, Connection connection) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return sqlExceptionHandler.isRetryable(ex, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Ensure type is in objecttypes table and get its assigned id
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Callers should note that this may commit a transaction and start a new one if a new type gets added
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long getTypeId(String type, Connection connection) throws SQLException, InternalServerErrorException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Exception detectedEx = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long typeId = readTypeId(type, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (typeId < 0) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle connection.setAutoCommit(true); // Commit the new type right away, and have no transaction isolation for read
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle createTypeId(type, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } catch (SQLException ex) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Rather than relying on DB specific ignore if exists functionality handle it here
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Could extend this in the future to more explicitly check for duplicate key error codes, but these again can be DB specific
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle detectedEx = ex;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle typeId = readTypeId(type, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (typeId < 0) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new InternalServerErrorException("Failed to populate and look up objecttypes table, no id could be retrieved for " + type, detectedEx);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle connection.setAutoCommit(false); // Start another transaction
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return typeId;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /**
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param type the object type URI
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the DB connection
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @return the typeId for the given type if exists, or -1 if does not exist
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws java.sql.SQLException
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long readTypeId(String type, Connection connection) throws SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long typeId = -1;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Map<String, Object> result = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ResultSet rs = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement readTypeStatement = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readTypeStatement = getPreparedStatement(connection, QueryDefinition.READTYPEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
10ce1c22b1cd83482f0127254fe8a27610bb25f5Brendan Miller logger.trace("Populating prepared statement {} for {}",
10ce1c22b1cd83482f0127254fe8a27610bb25f5Brendan Miller queryMap.get(QueryDefinition.READTYPEQUERYSTR), type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readTypeStatement.setString(1, type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", readTypeStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle rs = readTypeStatement.executeQuery();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (rs.next()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle typeId = rs.getLong("id");
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller logger.debug("Type: {}, id: {}", type, typeId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } finally {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(rs);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(readTypeStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return typeId;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /**
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param type the object type URI
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the DB connection
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @return true if a type was inserted
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws SQLException if the insert failed (e.g. concurrent insert by another thread)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean createTypeId(String type, Connection connection) throws SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement createTypeStatement = getPreparedStatement(connection, QueryDefinition.CREATETYPEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Create objecttype {}", type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle createTypeStatement.setString(1, type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", createTypeStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int val = createTypeStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return (val == 1);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } finally {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(createTypeStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /**
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Reads an object with for update locking applied
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Note: statement associated with the returned resultset
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * is not closed upon return.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * Aside from taking care to close the resultset it also is
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * the responsibility of the caller to close the associated
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * statement. Although the specification specifies that drivers/pools
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * should close the statement automatically, not all do this reliably.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle *
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param fullId qualified id of component type and id
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param type the component type
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param localId the id of the object within the component type
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @param connection the connection to use
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @return the row for the requested object, selected FOR UPDATE
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws NotFoundException if the requested object was not found in the DB
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @throws java.sql.SQLException for general DB issues
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public ResultSet readForUpdate(String fullId, String type, String localId, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws NotFoundException, SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement readForUpdateStatement = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ResultSet rs = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readForUpdateStatement = getPreparedStatement(connection, QueryDefinition.READFORUPDATEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Populating prepared statement {} for {}", readForUpdateStatement, fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readForUpdateStatement.setString(1, type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle readForUpdateStatement.setString(2, localId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Executing: {}", readForUpdateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle rs = readForUpdateStatement.executeQuery();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (rs.next()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Read for update full id: {}", fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return rs;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(rs);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(readForUpdateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new NotFoundException("Object " + fullId + " not found in " + type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } catch (SQLException ex) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(rs);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(readForUpdateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw ex;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* (non-Javadoc)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @see org.forgerock.openidm.repo.jdbc.impl.TableHandler#update(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.util.Map, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle @Override
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public void update(String fullId, String type, String localId, String rev, Map<String, Object> obj, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws SQLException, IOException, PreconditionFailedException, NotFoundException, InternalServerErrorException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Update with fullid {}", fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int revInt = Integer.parseInt(rev);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ++revInt;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String newRev = Integer.toString(revInt);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_rev", newRev); // Save the rev in the object, and return the changed rev from the create.
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ResultSet rs = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement updateStatement = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement deletePropStatement = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle rs = readForUpdate(fullId, type, localId, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String existingRev = rs.getString("rev");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long dbId = rs.getLong("id");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle long objectTypeDbId = rs.getLong("objecttypes_id");
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller logger.debug("Update existing object {} rev: {} db id: {}, object type db id: {}", fullId, existingRev, dbId, objectTypeDbId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (!existingRev.equals(rev)) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new PreconditionFailedException("Update rejected as current Object revision " + existingRev + " is different than expected by caller (" + rev + "), the object has changed since retrieval.");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle updateStatement = getPreparedStatement(connection, QueryDefinition.UPDATEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deletePropStatement = getPreparedStatement(connection, QueryDefinition.PROPDELETEQUERYSTR);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Support changing object identifier
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String newLocalId = (String) obj.get("_id");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (newLocalId != null && !localId.equals(newLocalId)) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Object identifier is changing from " + localId + " to " + newLocalId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle newLocalId = localId; // If it hasn't changed, use the existing ID
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_id", newLocalId); // Ensure the ID is saved in the object
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String objString = mapper.writeValueAsString(obj);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller logger.trace("Populating prepared statement {} for {} {} {} {} {}", updateStatement, fullId, newLocalId, newRev, objString, dbId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle updateStatement.setString(1, newLocalId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle updateStatement.setString(2, newRev);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle updateStatement.setString(3, objString);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle updateStatement.setLong(4, dbId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Update statement: {}", updateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int updateCount = updateStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Updated rows: {} for {}", updateCount, fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (updateCount != 1) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new InternalServerErrorException("Update execution did not result in updating 1 row as expected. Updated rows: " + updateCount);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle JsonValue jv = new JsonValue(obj);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // TODO: only update what changed?
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller logger.trace("Populating prepared statement {} for {} {} {}", deletePropStatement, fullId, type, localId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deletePropStatement.setString(1, type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deletePropStatement.setString(2, localId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Update properties del statement: {}", deletePropStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int deleteCount = deletePropStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Deleted child rows: {} for: {}", deleteCount, fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle writeValueProperties(fullId, dbId, localId, jv, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } finally {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (rs != null) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Ensure associated statement also is closed
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Statement rsStatement = rs.getStatement();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(rs);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(rsStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(updateStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(deletePropStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /**
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * @see org.forgerock.openidm.repo.jdbc.impl.GenericTableHandler#delete(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle @Override
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public void delete(String fullId, String type, String localId, String rev, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws PreconditionFailedException, InternalServerErrorException, NotFoundException, SQLException, IOException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Delete with fullid {}", fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // First check if the revision matches and select it for UPDATE
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ResultSet existing = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle PreparedStatement deleteStatement = null;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle try {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle existing = readForUpdate(fullId, type, localId, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } catch (NotFoundException ex) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new NotFoundException("Object does not exist for delete on: " + fullId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String existingRev = existing.getString("rev");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (!"*".equals(rev) && !rev.equals(existingRev)) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new PreconditionFailedException("Delete rejected as current Object revision " + existingRev + " is different than "
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle + "expected by caller " + rev + ", the object has changed since retrieval.");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Proceed with the valid delete
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deleteStatement = getPreparedStatement(connection, QueryDefinition.DELETEQUERYSTR);
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller logger.trace("Populating prepared statement {} for {} {} {} {}", deleteStatement, fullId, type, localId, rev);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Rely on ON DELETE CASCADE for connected object properties to be deleted
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deleteStatement.setString(1, type);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deleteStatement.setString(2, localId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle deleteStatement.setString(3, rev);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("Delete statement: {}", deleteStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle int deletedRows = deleteStatement.executeUpdate();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Deleted {} rows for id : {} {}", deletedRows, localId);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (deletedRows < 1) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throw new InternalServerErrorException("Deleting object for " + fullId + " failed, DB reported " + deletedRows + " rows deleted");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.debug("delete for id succeeded: {} revision: {}", localId, rev);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } finally {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (existing != null) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Ensure associated statement also is closed
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Statement existingStatement = existing.getStatement();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(existing);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(existingStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle CleanupHelper.loggedClose(deleteStatement);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle /* (non-Javadoc)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle * @see org.forgerock.openidm.repo.jdbc.impl.TableHandler#delete(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.sql.Connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle */
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle @Override
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public List<Map<String, Object>> query(String type, Map<String, Object> params, Connection connection)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws ResourceException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return queries.query(type, params, connection);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller @Override
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller public Integer command(String type, Map<String, Object> params, Connection connection) throws SQLException, ResourceException {
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller return queries.command(type, params, connection);
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller }
79d12e9411155c9e79c08df9bb6412b2862e6064Brendan Mmiller
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle @Override
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public String toString() {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return "Generic handler mapped to [" + mainTableName + ", " + propTableName + "]";
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle protected PreparedStatement getPreparedStatement(Connection connection, QueryDefinition queryDefinition) throws SQLException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return queries.getPreparedStatement(connection, queryMap.get(queryDefinition));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller /**
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * Render and SQL SELECT statement with placeholders for the given query filter.
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller *
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * @param filter the query filter
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * @param replacementTokens a map to store any replacement tokens
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * @param params a map containing query parameters
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * @return an SQL SELECT statement
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller */
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle @Override
af9b081b11f86e7fddd67382b1db65dbd5650332Jim Mitchener public String renderQueryFilter(QueryFilter<JsonPointer> filter, Map<String, Object> replacementTokens, Map<String, Object> params) {
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller final int offsetParam = Integer.parseInt((String) params.get(PAGED_RESULTS_OFFSET));
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller final int pageSizeParam = Integer.parseInt((String) params.get(PAGE_SIZE));
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller SQLBuilder builder = new SQLBuilder() {
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller @Override
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller public String toSQL() {
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller return "SELECT " + getColumns().toSQL()
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller + getFromClause().toSQL()
0743258ac0c62d8717cc7765bca394618e64af0fBrendan Mmiller + getJoinClause().toSQL()
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller + getWhereClause().toSQL()
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller + getOrderByClause().toSQL()
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller + " LIMIT " + pageSizeParam
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller + " OFFSET " + offsetParam;
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller }
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller };
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller // "SELECT obj.* FROM mainTable obj..."
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller builder.addColumn("obj.*")
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller .from("${_dbSchema}.${_mainTable} obj")
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller // join objecttypes to fix OPENIDM-2773
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller .join("${_dbSchema}.objecttypes", "objecttypes")
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller .on(where("obj.objecttypes_id = objecttypes.id")
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller .and("objecttypes.objecttype = ${otype}"))
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller // construct where clause by visiting filter
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller .where(filter.accept(new GenericSQLQueryFilterVisitor(SEARCHABLE_LENGTH, builder), replacementTokens));
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller // other half of OPENIDM-2773 fix
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller replacementTokens.put("otype", params.get("_resource"));
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller // JsonValue-cheat to avoid an unchecked cast
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller final List<SortKey> sortKeys = new JsonValue(params).get(SORT_KEYS).asList(SortKey.class);
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle // Check for sort keys and build up order-by syntax
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller prepareSortKeyStatements(builder, sortKeys, replacementTokens);
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller return builder.toSQL();
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle }
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle /**
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle * Loops through sort keys constructing the inner join and key statements.
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller *
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * @param builder the SQL builder
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * @param sortKeys a {@link java.util.List} of sort keys
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller * @param replacementTokens a {@link java.util.Map} containing replacement tokens for the {@link java.sql.PreparedStatement}
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle */
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller protected void prepareSortKeyStatements(SQLBuilder builder, List<SortKey> sortKeys, Map<String, Object> replacementTokens) {
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller if (sortKeys == null) {
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller return;
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller }
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle for (int i = 0; i < sortKeys.size(); i++) {
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle final SortKey sortKey = sortKeys.get(i);
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle final String tokenName = "sortKey" + i;
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle final String tableAlias = "orderby" + i;
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller builder.join("${_dbSchema}.${_propTable}", tableAlias)
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller .on(where(tableAlias + ".${_mainTable}_id = obj.id").and(tableAlias + ".propkey = ${" + tokenName + "}"))
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller .orderBy(tableAlias + ".propvalue", sortKey.isAscendingOrder());
5c6fc9459842796234027c8bb8f58886d69ebc8fBrendan Mmiller
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle replacementTokens.put(tokenName, sortKey.getField().toString());
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle }
a32c32954ffe171a0437ff1bc1287e9086f0bd26Chad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle}
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleclass GenericQueryResultMapper implements QueryResultMapper {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle final static Logger logger = LoggerFactory.getLogger(GenericQueryResultMapper.class);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Jackson parser
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ObjectMapper mapper = new ObjectMapper();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Type information for the Jackson parser
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle TypeReference<LinkedHashMap<String,Object>> typeRef = new TypeReference<LinkedHashMap<String,Object>>() {};
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public List<Map<String, Object>> mapQueryToObject(ResultSet rs, String queryId, String type, Map<String, Object> params, TableQueries tableQueries)
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle throws SQLException, IOException {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle ResultSetMetaData rsMetaData = rs.getMetaData();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasFullObject = tableQueries.hasColumn(rsMetaData, "fullobject");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasId = false;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasRev = false;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasPropKey = false;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle boolean hasPropValue = false;
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener boolean hasTotal = false;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (!hasFullObject) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle hasId = tableQueries.hasColumn(rsMetaData, "objectid");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle hasRev = tableQueries.hasColumn(rsMetaData, "rev");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle hasPropKey = tableQueries.hasColumn(rsMetaData, "propkey");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle hasPropValue = tableQueries.hasColumn(rsMetaData, "propvalue");
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener hasTotal = tableQueries.hasColumn(rsMetaData, "total");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle while (rs.next()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (hasFullObject) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String objString = rs.getString("fullobject");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Map<String, Object> obj = mapper.readValue(objString, typeRef);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // TODO: remove data logging
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle logger.trace("Query result for queryId: {} type: {} converted obj: {}", new Object[] {queryId, type, obj});
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.add(obj);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Map<String, Object> obj = new HashMap<String, Object>();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (hasId) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_id", rs.getString("objectid"));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (hasRev) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle obj.put("_rev", rs.getString("rev"));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener if (hasTotal) {
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener obj.put("total", rs.getInt("total"));
117941f80637bf0fe66cbb3a444430ef88f629a7Jim Mitchener }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle // Results from query on individual searchable property
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (hasPropKey && hasPropValue) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle String propKey = rs.getString("propkey");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle Object propValue = rs.getObject("propvalue");
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle JsonPointer pointer = new JsonPointer(propKey);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle JsonValue wrapped = new JsonValue(obj);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle wrapped.put(pointer, propValue);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle result.add(obj);
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return result;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle}
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleclass GenericTableConfig {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public String mainTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public String propertiesTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean searchableDefault;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public GenericPropertiesConfig properties;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean isSearchable(JsonPointer propPointer) {
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff // More specific configuration takes precedence
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff Boolean explicit = null;
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff while (!propPointer.isEmpty() && explicit == null) {
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff explicit = properties.explicitlySearchable.get(propPointer);
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff propPointer = propPointer.parent();
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff }
e441210fb6e4bab95f05f24644e4d2ed152efd8cAndi Egloff
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (explicit != null) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return explicit.booleanValue();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle } else {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return searchableDefault;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff /**
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff * @return Approximation on whether this may have searchable properties
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff * It is only an approximation as we do not have an exhaustive list of possible properties
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff * to consider against a default setting of searchable.
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff */
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff public boolean hasPossibleSearchableProperties() {
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff return ((searchableDefault) ? true : properties.explicitSearchableProperties);
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public static GenericTableConfig parse(JsonValue tableConfig) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle GenericTableConfig cfg = new GenericTableConfig();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle tableConfig.required();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle cfg.mainTableName = tableConfig.get("mainTable").required().asString();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle cfg.propertiesTableName = tableConfig.get("propertiesTable").required().asString();
1e7cf46779cd3edd805fffdd3e5c3c8f5d36a75cBrendan Mmiller cfg.searchableDefault = tableConfig.get("searchableDefault").defaultTo(Boolean.TRUE).asBoolean();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle cfg.properties = GenericPropertiesConfig.parse(tableConfig.get("properties"));
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return cfg;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle}
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienleclass GenericPropertiesConfig {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public Map<JsonPointer, Boolean> explicitlySearchable = new HashMap<JsonPointer, Boolean>();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public String mainTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public String propertiesTableName;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public boolean searchableDefault;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public GenericPropertiesConfig properties;
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff // Whether there are any properties explicitly set to searchable true
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff public boolean explicitSearchableProperties;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle public static GenericPropertiesConfig parse(JsonValue propsConfig) {
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle GenericPropertiesConfig cfg = new GenericPropertiesConfig();
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle if (!propsConfig.isNull()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle for (String propName : propsConfig.keys()) {
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle JsonValue detail = propsConfig.get(propName);
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff boolean propSearchable = detail.get("searchable").asBoolean();
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff cfg.explicitlySearchable.put(new JsonPointer(propName), propSearchable);
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff if (propSearchable) {
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff cfg.explicitSearchableProperties = true;
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
9f1e7c4b37140496d6fb5ad0a3ae507e4f9db37cAndi Egloff
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle return cfg;
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle }
37f9df7d5b474a12668813f98992dceb7c7feacbChad Kienle}