http://git-wip-us.apache.org/repos/asf/marmotta/blob/796c598e/libraries/kiwi/kiwi-triplestore/src/main/java/org/apache/marmotta/kiwi/persistence/KiWiConnection.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-triplestore/src/main/java/org/apache/marmotta/kiwi/persistence/KiWiConnection.java b/libraries/kiwi/kiwi-triplestore/src/main/java/org/apache/marmotta/kiwi/persistence/KiWiConnection.java index 712973c..915243c 100644 --- a/libraries/kiwi/kiwi-triplestore/src/main/java/org/apache/marmotta/kiwi/persistence/KiWiConnection.java +++ b/libraries/kiwi/kiwi-triplestore/src/main/java/org/apache/marmotta/kiwi/persistence/KiWiConnection.java @@ -1,19 +1,18 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. */ package org.apache.marmotta.kiwi.persistence; @@ -48,23 +47,22 @@ import java.util.Date; import java.util.concurrent.locks.ReentrantLock; /** - * A KiWiConnection offers methods for storing and retrieving KiWiTriples, KiWiNodes, and KiWiNamespaces in the - * database. It wraps a JDBC connection which will be committed on commit(), rolled back on rollback() and - * closed on close(); + * A KiWiConnection offers methods for storing and retrieving KiWiTriples, + * KiWiNodes, and KiWiNamespaces in the database. It wraps a JDBC connection + * which will be committed on commit(), rolled back on rollback() and closed on + * close(); * <p/> * Author: Sebastian Schaffert */ -@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") public class KiWiConnection implements AutoCloseable { private static Logger log = LoggerFactory.getLogger(KiWiConnection.class); - protected KiWiDialect dialect; protected Connection connection; - protected KiWiPersistence persistence; + protected KiWiPersistence persistence; protected CacheManager cacheManager; @@ -73,47 +71,47 @@ public class KiWiConnection implements AutoCloseable { /** * Cache nodes by database ID */ - private Map<Long,KiWiNode> nodeCache; + private Map<Long, KiWiNode> nodeCache; /** * Cache triples by database ID */ - private Map<Long,KiWiTriple> tripleCache; + private Map<Long, KiWiTriple> tripleCache; /** * Cache URI resources by uri */ - private Map<String,KiWiUriResource> uriCache; + private Map<String, KiWiUriResource> uriCache; /** * Cache BNodes by BNode ID */ - private Map<String,KiWiAnonResource> bnodeCache; + private Map<String, KiWiAnonResource> bnodeCache; /** - * Cache literals by literal cache key (LiteralCommons#createCacheKey(String,Locale,URI)) + * Cache literals by literal cache key + * (LiteralCommons#createCacheKey(String,Locale,URI)) */ - private Map<String,KiWiLiteral> literalCache; + private Map<String, KiWiLiteral> literalCache; /** * Look up namespaces by URI */ - private Map<String,KiWiNamespace> namespaceUriCache; + private Map<String, KiWiNamespace> namespaceUriCache; /** * Look up namespaces by prefix */ - private Map<String,KiWiNamespace> namespacePrefixCache; + private Map<String, KiWiNamespace> namespacePrefixCache; /** * Cache instances of locales for language tags */ - private static Map<String,Locale> localeMap = new HashMap<>(); + private static Map<String, Locale> localeMap = new HashMap<String, Locale>(); private static Calendar calendarUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - - private Map<String,PreparedStatement> statementCache; + private Map<String, PreparedStatement> statementCache; private boolean autoCommit = false; @@ -142,13 +140,13 @@ public class KiWiConnection implements AutoCloseable { public KiWiConnection(KiWiPersistence persistence, KiWiDialect dialect, CacheManager cacheManager) throws SQLException { this.cacheManager = cacheManager; - this.dialect = dialect; - this.persistence = persistence; - this.commitLock = new ReentrantLock(); - this.literalLock = new ReentrantLock(); - this.uriLock = new ReentrantLock(); - this.bnodeLock = new ReentrantLock(); - this.batchCommit = dialect.isBatchSupported(); + this.dialect = dialect; + this.persistence = persistence; + this.commitLock = new ReentrantLock(); + this.literalLock = new ReentrantLock(); + this.uriLock = new ReentrantLock(); + this.bnodeLock = new ReentrantLock(); + this.batchCommit = dialect.isBatchSupported(); this.deletedStatementsLog = BloomFilter.create(Funnels.longFunnel(), 100000); this.transactionId = getNextSequence(); @@ -157,48 +155,50 @@ public class KiWiConnection implements AutoCloseable { } private void initCachePool() { - nodeCache = cacheManager.getNodeCache(); - tripleCache = cacheManager.getTripleCache(); - uriCache = cacheManager.getUriCache(); - bnodeCache = cacheManager.getBNodeCache(); + nodeCache = cacheManager.getNodeCache(); + tripleCache = cacheManager.getTripleCache(); + uriCache = cacheManager.getUriCache(); + bnodeCache = cacheManager.getBNodeCache(); literalCache = cacheManager.getLiteralCache(); namespacePrefixCache = cacheManager.getNamespacePrefixCache(); - namespaceUriCache = cacheManager.getNamespaceUriCache(); + namespaceUriCache = cacheManager.getNamespaceUriCache(); } /** * Load all prepared statements of the dialect into the statement cache + * * @throws SQLException */ private void initStatementCache() throws SQLException { - statementCache = new HashMap<>(); + statementCache = new HashMap<String, PreparedStatement>(); /* - for(String key : dialect.getStatementIdentifiers()) { - statementCache.put(key,connection.prepareStatement(dialect.getStatement(key))); - } - */ + for(String key : dialect.getStatementIdentifiers()) { + statementCache.put(key,connection.prepareStatement(dialect.getStatement(key))); + } + */ } /** - * This method must be called by all methods as soon as they actually require a JDBC connection. This allows - * more efficient implementations in case the queries can be answered directly from the cache. + * This method must be called by all methods as soon as they actually + * require a JDBC connection. This allows more efficient implementations in + * case the queries can be answered directly from the cache. */ protected void requireJDBCConnection() throws SQLException { - if(connection == null) { + if (connection == null) { connection = persistence.getJDBCConnection(); connection.setAutoCommit(autoCommit); } - if(tripleBatch == null) { - tripleBatch = new TripleTable<>(); + if (tripleBatch == null) { + tripleBatch = new TripleTable<KiWiTriple>(); } } /** * Get direct access to the JDBC connection used by this KiWiConnection. * - * @return the underlying raw JDBC connection + * @return */ public Connection getJDBCConnection() throws SQLException { requireJDBCConnection(); @@ -208,7 +208,8 @@ public class KiWiConnection implements AutoCloseable { /** * Return the cache manager used by this connection - * @return the CacheManager + * + * @return */ public CacheManager getCacheManager() { return cacheManager; @@ -223,17 +224,18 @@ public class KiWiConnection implements AutoCloseable { } /** - * Load a KiWiNamespace with the given prefix, or null if the namespace does not exist. The method will first - * look in the node cache for cached nodes. If no cache entry is found, it will run a database query + * Load a KiWiNamespace with the given prefix, or null if the namespace does + * not exist. The method will first look in the node cache for cached nodes. + * If no cache entry is found, it will run a database query * ("load.namespace_prefix"). * - * @param prefix the prefix to look for + * @param prefix the prefix to look for * @return the KiWiNamespace with this prefix or null if it does not exist * @throws SQLException */ public KiWiNamespace loadNamespaceByPrefix(String prefix) throws SQLException { KiWiNamespace element = namespacePrefixCache.get(prefix); - if(element != null) { + if (element != null) { return element; } @@ -246,27 +248,31 @@ public class KiWiConnection implements AutoCloseable { // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return constructNamespaceFromDatabase(result); } else { return null; } + } finally { + result.close(); } } /** - * Load a KiWiNamespace with the given uri, or null if the namespace does not exist. The method will first - * look in the node cache for cached nodes. If no cache entry is found, it will run a database query + * Load a KiWiNamespace with the given uri, or null if the namespace does + * not exist. The method will first look in the node cache for cached nodes. + * If no cache entry is found, it will run a database query * ("load.namespace_prefix"). * - * @param uri the uri to look for + * @param uri the uri to look for * @return the KiWiNamespace with this uri or null if it does not exist * @throws SQLException */ public KiWiNamespace loadNamespaceByUri(String uri) throws SQLException { KiWiNamespace element = namespaceUriCache.get(uri); - if(element != null) { + if (element != null) { return element; } @@ -279,26 +285,31 @@ public class KiWiConnection implements AutoCloseable { // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return constructNamespaceFromDatabase(result); } else { return null; } + } finally { + result.close(); } } /** - * Store the namespace passed as argument in the database. The database might enfore unique constraints and - * thus throw an exception in case the prefix or URI is already used. + * Store the namespace passed as argument in the database. The database + * might enfore unique constraints and thus throw an exception in case the + * prefix or URI is already used. * * @param namespace the namespace to store - * @throws SQLException the prefix or URI is already used, or a database error occurred + * @throws SQLException the prefix or URI is already used, or a database + * error occurred */ public void storeNamespace(KiWiNamespace namespace) throws SQLException { // TODO: add unique constraints to table - if(namespace.getId() >= 0) { - log.warn("trying to store namespace which is already persisted: {}",namespace); + if (namespace.getId() >= 0) { + log.warn("trying to store namespace which is already persisted: {}", namespace); return; } @@ -307,25 +318,27 @@ public class KiWiConnection implements AutoCloseable { namespace.setId(getNextSequence()); PreparedStatement insertNamespace = getPreparedStatement("store.namespace"); - insertNamespace.setLong(1,namespace.getId()); - insertNamespace.setString(2,namespace.getPrefix()); - insertNamespace.setString(3,namespace.getUri()); - insertNamespace.setTimestamp(4,new Timestamp(namespace.getCreated().getTime())); + insertNamespace.setLong(1, namespace.getId()); + insertNamespace.setString(2, namespace.getPrefix()); + insertNamespace.setString(3, namespace.getUri()); + insertNamespace.setTimestamp(4, new Timestamp(namespace.getCreated().getTime())); insertNamespace.executeUpdate(); namespacePrefixCache.put(namespace.getPrefix(), namespace); - namespaceUriCache.put(namespace.getUri(),namespace); + namespaceUriCache.put(namespace.getUri(), namespace); } /** - * Delete the namespace passed as argument from the database and from the caches. + * Delete the namespace passed as argument from the database and from the + * caches. + * * @param namespace the namespace to delete * @throws SQLException in case a database error occurred */ public void deleteNamespace(KiWiNamespace namespace) throws SQLException { - if(namespace.getId() < 0) { - log.warn("trying to remove namespace which is not persisted: {}",namespace); + if (namespace.getId() < 0) { + log.warn("trying to remove namespace which is not persisted: {}", namespace); return; } @@ -341,105 +354,119 @@ public class KiWiConnection implements AutoCloseable { /** * Count all non-deleted triples in the triple store - * @return number of non-deleted triples in the triple store + * + * @return * @throws SQLException */ public long getSize() throws SQLException { requireJDBCConnection(); PreparedStatement querySize = getPreparedStatement("query.size"); - try (ResultSet result = querySize.executeQuery()) { + ResultSet result = querySize.executeQuery(); + try { if (result.next()) { return result.getLong(1) + (tripleBatch != null ? tripleBatch.size() : 0); } else { - return tripleBatch != null ? tripleBatch.size() : 0; + return 0 + (tripleBatch != null ? tripleBatch.size() : 0); } + } finally { + result.close(); } } /** * Count all non-deleted triples in the triple store - * @param context the context to count - * @return number of non-deleted triples in the provided context + * + * @return * @throws SQLException */ public long getSize(KiWiResource context) throws SQLException { - if(context.getId() < 0) { + if (context.getId() < 0) { return 0; - } + }; requireJDBCConnection(); PreparedStatement querySize = getPreparedStatement("query.size_ctx"); - querySize.setLong(1,context.getId()); + querySize.setLong(1, context.getId()); - try (ResultSet result = querySize.executeQuery()) { + ResultSet result = querySize.executeQuery(); + try { if (result.next()) { return result.getLong(1) + (tripleBatch != null ? tripleBatch.listTriples(null, null, null, context, false).size() : 0); } else { - return tripleBatch != null ? tripleBatch.listTriples(null, null, null, context, false).size() : 0; + return 0 + (tripleBatch != null ? tripleBatch.listTriples(null, null, null, context, false).size() : 0); } + } finally { + result.close(); } } /** - * Load a KiWiNode by database ID. The method will first look in the node cache for cached nodes. If - * no cache entry is found, it will run a database query ('load.node_by_id') on the NODES table and - * construct an appropriate subclass instance of KiWiNode with the obtained values. The result will be - * constructed based on the value of the NTYPE column as follows: + * Load a KiWiNode by database ID. The method will first look in the node + * cache for cached nodes. If no cache entry is found, it will run a + * database query ('load.node_by_id') on the NODES table and construct an + * appropriate subclass instance of KiWiNode with the obtained values. The + * result will be constructed based on the value of the NTYPE column as + * follows: * <ul> - * <li>'uri' - KiWiUriResource using the id and svalue (as URI) columns</li> - * <li>'bnode' - KiWiAnonResource using the id and svalue (as AnonId) columns</li> - * <li>'string' - KiWiStringLiteral using the id, svalue (literal value), lang (literal - * language) and ltype (literal type) columns</li> - * <li>'int' - KiWiIntLiteral using the id, svalue (string value), ivalue (integer value) - * and ltype (literal type) columns</li> - * <li>'double' - KiWiDoubleLiteral using the id, svalue (string value), dvalue (double - * value) and ltype (literal type) columns</li> - * <li>'boolean' - KiWiBooleanLiteral using the id, svalue (string value), bvalue (boolean - * value) and ltype (literal type) columns</li> - * <li>'date' - KiWiDateLiteral using the id, svalue (string value), tvalue (time value) - * and ltype (literal type) columns</li> + * <li>'uri' - KiWiUriResource using the id and svalue (as URI) columns</li> + * <li>'bnode' - KiWiAnonResource using the id and svalue (as AnonId) + * columns</li> + * <li>'string' - KiWiStringLiteral using the id, svalue (literal value), + * lang (literal language) and ltype (literal type) columns</li> + * <li>'int' - KiWiIntLiteral using the id, svalue (string value), ivalue + * (integer value) and ltype (literal type) columns</li> + * <li>'double' - KiWiDoubleLiteral using the id, svalue (string value), + * dvalue (double value) and ltype (literal type) columns</li> + * <li>'boolean' - KiWiBooleanLiteral using the id, svalue (string value), + * bvalue (boolean value) and ltype (literal type) columns</li> + * <li>'date' - KiWiDateLiteral using the id, svalue (string value), tvalue + * (time value) and ltype (literal type) columns</li> * </ul> - * When a node is loaded from the database, it will be added to the different caches to speed up - * subsequent requests. + * When a node is loaded from the database, it will be added to the + * different caches to speed up subsequent requests. * * @param id the database id of the node to load - * @return an instance of a KiWiNode subclass representing the node with the given database id; - * type depends on value of the ntype column + * @return an instance of a KiWiNode subclass representing the node with the + * given database id; type depends on value of the ntype column */ public KiWiNode loadNodeById(long id) throws SQLException { // look in cache KiWiNode element = nodeCache.get(id); - if(element != null) { + if (element != null) { return element; } requireJDBCConnection(); // prepare a query; we will only iterate once, read only, and need only one result row since the id is unique - final PreparedStatement query = getPreparedStatement("load.node_by_id"); + PreparedStatement query = getPreparedStatement("load.node_by_id"); synchronized (query) { - query.setLong(1,id); + query.setLong(1, id); query.setMaxRows(1); // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return constructNodeFromDatabase(result); } else { return null; } + } finally { + result.close(); } } } /** - * Batch load the nodes with the given ids. This method aims to offer performance improvements by reducing - * database roundtrips. + * Batch load the nodes with the given ids. This method aims to offer + * performance improvements by reducing database roundtrips. + * * @param ids array of ids to retrieve * @return array of nodes corresponding to these ids (in the same order) * @throws SQLException @@ -451,36 +478,37 @@ public class KiWiConnection implements AutoCloseable { // first look in the cache for any ids that have already been loaded ArrayList<Long> toFetch = new ArrayList<>(ids.length); - for(int i=0; i < ids.length; i++) { - if(ids[i] != 0) { + for (int i = 0; i < ids.length; i++) { + if (ids[i] != 0) { result[i] = nodeCache.get(ids[i]); - if(result[i] == null) { + if (result[i] == null) { toFetch.add(ids[i]); } } } - if(toFetch.size() > 0) { + if (toFetch.size() > 0) { // declare variables before to optimize stack allocation int position = 0; int nextBatchSize; PreparedStatement query; KiWiNode node; - while(position < toFetch.size()) { + while (position < toFetch.size()) { nextBatchSize = computeBatchSize(position, toFetch.size()); query = getPreparedStatement("load.nodes_by_ids", nextBatchSize); synchronized (query) { - for(int i=0; i<nextBatchSize; i++) { - query.setLong(i+1, toFetch.get(position + i)); + for (int i = 0; i < nextBatchSize; i++) { + query.setLong(i + 1, toFetch.get(position + i)); } query.setMaxRows(nextBatchSize); // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet rows = query.executeQuery()) { + ResultSet rows = query.executeQuery(); + try { while (rows.next()) { node = constructNodeFromDatabase(rows); for (int i = 0; i < ids.length; i++) { @@ -489,20 +517,21 @@ public class KiWiConnection implements AutoCloseable { } } } + } finally { + rows.close(); } position += nextBatchSize; } } - } return result; } private int computeBatchSize(int position, int length) { int batchSize = QUERY_BATCH_SIZE; - while(length - position < batchSize) { + while (length - position < batchSize) { batchSize = batchSize >> 1; } return batchSize; @@ -512,7 +541,7 @@ public class KiWiConnection implements AutoCloseable { // look in cache KiWiTriple element = tripleCache.get(id); - if(element != null) { + if (element != null) { return element; } @@ -520,38 +549,43 @@ public class KiWiConnection implements AutoCloseable { // prepare a query; we will only iterate once, read only, and need only one result row since the id is unique PreparedStatement query = getPreparedStatement("load.triple_by_id"); - query.setLong(1,id); + query.setLong(1, id); query.setMaxRows(1); // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return constructTripleFromDatabase(result); } else { return null; } + } finally { + result.close(); } } /** - * Load a KiWiUriResource by URI. The method will first look in the node cache for cached nodes. If - * no cache entry is found, it will run a database query ('load.uri_by_uri') on the NODES table and - * construct a new KiWiUriResource using the values of the id and svalue columns. + * Load a KiWiUriResource by URI. The method will first look in the node + * cache for cached nodes. If no cache entry is found, it will run a + * database query ('load.uri_by_uri') on the NODES table and construct a new + * KiWiUriResource using the values of the id and svalue columns. * <p/> - * When a node is loaded from the database, it will be added to the different caches to speed up - * subsequent requests. + * When a node is loaded from the database, it will be added to the + * different caches to speed up subsequent requests. * * @param uri the URI of the resource to load - * @return the KiWiUriResource identified by the given URI or null if it does not exist + * @return the KiWiUriResource identified by the given URI or null if it + * does not exist */ public KiWiUriResource loadUriResource(String uri) throws SQLException { Preconditions.checkNotNull(uri); // look in cache KiWiUriResource element = uriCache.get(uri); - if(element != null) { + if (element != null) { return element; } @@ -566,12 +600,15 @@ public class KiWiConnection implements AutoCloseable { // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return (KiWiUriResource) constructNodeFromDatabase(result); } else { return null; } + } finally { + result.close(); } } finally { uriLock.unlock(); @@ -579,21 +616,23 @@ public class KiWiConnection implements AutoCloseable { } /** - * Load a KiWiAnonResource by anonymous ID. The method will first look in the node cache for - * cached nodes. If no cache entry is found, it will run a database query ('load.bnode_by_anonid') - * on the NODES table and construct a new KiWiAnonResource using the values of the id and - * svalue columns. + * Load a KiWiAnonResource by anonymous ID. The method will first look in + * the node cache for cached nodes. If no cache entry is found, it will run + * a database query ('load.bnode_by_anonid') on the NODES table and + * construct a new KiWiAnonResource using the values of the id and svalue + * columns. * <p/> - * When a node is loaded from the database, it will be added to the different caches to speed up - * subsequent requests. + * When a node is loaded from the database, it will be added to the + * different caches to speed up subsequent requests. * * @param id the anonymous ID of the resource to load - * @return the KiWiAnonResource identified by the given internal ID or null if it does not exist + * @return the KiWiAnonResource identified by the given internal ID or null + * if it does not exist */ public KiWiAnonResource loadAnonResource(String id) throws SQLException { // look in cache KiWiAnonResource element = bnodeCache.get(id); - if(element != null) { + if (element != null) { return element; } @@ -604,17 +643,20 @@ public class KiWiConnection implements AutoCloseable { try { // prepare a query; we will only iterate once, read only, and need only one result row since the id is unique PreparedStatement query = getPreparedStatement("load.bnode_by_anonid"); - query.setString(1,id); + query.setString(1, id); query.setMaxRows(1); // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return (KiWiAnonResource) constructNodeFromDatabase(result); } else { return null; } + } finally { + result.close(); } } finally { bnodeLock.unlock(); @@ -622,31 +664,35 @@ public class KiWiConnection implements AutoCloseable { } /** - * Load a literal based on the value, language and type passed as argument. The method will first look in the node cache for - * cached nodes. If no cache entry is found, it will run a database query ("load.literal_by_v") - * on the NODES table and construct a new KiWiLiteral using the values of the literal columns (svalue, ivalue, ...). The - * type of literal returned depends on the value of the ntype column. + * Load a literal based on the value, language and type passed as argument. + * The method will first look in the node cache for cached nodes. If no + * cache entry is found, it will run a database query ("load.literal_by_v") + * on the NODES table and construct a new KiWiLiteral using the values of + * the literal columns (svalue, ivalue, ...). The type of literal returned + * depends on the value of the ntype column. * <p/> - * When a node is loaded from the database, it will be added to the different caches to speed up - * subsequent requests. + * When a node is loaded from the database, it will be added to the + * different caches to speed up subsequent requests. * * @param value string value of the literal to load - * @param lang language of the literal to load (optional, 2-letter language code with optional country) + * @param lang language of the literal to load (optional, 2-letter language + * code with optional country) * @param ltype the type of the literal to load (optional) - * @return the literal matching the given arguments or null if it does not exist + * @return the literal matching the given arguments or null if it does not + * exist * @throws SQLException */ public KiWiLiteral loadLiteral(String value, String lang, KiWiUriResource ltype) throws SQLException { // look in cache - final KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(value,getLocale(lang), ltype)); - if(element != null) { + final KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(value, getLocale(lang), ltype)); + if (element != null) { return element; } requireJDBCConnection(); // ltype not persisted - if(ltype != null && ltype.getId() < 0) { + if (ltype != null && ltype.getId() < 0) { return null; } @@ -655,17 +701,89 @@ public class KiWiConnection implements AutoCloseable { try { // otherwise prepare a query, depending on the parameters given final PreparedStatement query; - if(lang == null && ltype == null) { + if (lang == null && ltype == null) { query = getPreparedStatement("load.literal_by_v"); - query.setString(1,value); - } else if(lang != null) { + query.setString(1, value); + } else if (lang != null) { query = getPreparedStatement("load.literal_by_vl"); - query.setString(1,value); + query.setString(1, value); query.setString(2, lang); - } else if(ltype != null) { + } else if (ltype != null) { query = getPreparedStatement("load.literal_by_vt"); - query.setString(1,value); - query.setLong(2,ltype.getId()); + query.setString(1, value); + query.setLong(2, ltype.getId()); + } else { + // This cannot happen... + throw new IllegalArgumentException("Impossible combination of lang/type in loadLiteral!"); + } + + // run the database query and if it yields a result, construct a new node; the method call will take care of + // caching the constructed node for future calls + ResultSet result = query.executeQuery(); + try { + if (result.next()) { + return (KiWiLiteral) constructNodeFromDatabase(result); + } else { + return null; + } + } finally { + result.close(); + } + } finally { + literalLock.unlock(); + } + } + + /** + * Load a literal based on the value, language and type passed as argument. + * The method will first look in the node cache for cached nodes. If no + * cache entry is found, it will run a database query ("load.literal_by_g") + * on the NODES table and construct a new KiWiLiteral using the values of + * the literal columns (svalue, ivalue, ...). The type of literal returned + * depends on the value of the ntype column. + * <p/> + * When a node is loaded from the database, it will be added to the + * different caches to speed up subsequent requests. + * + * @param isGeo only to diference: string literal from geo literal + * @param value string value of the geometry literal to load + * @param lang language of the literal to load (optional, 2-letter language + * code with optional country) + * @param ltype the type of the literal to load (optional) + * @return the literal matching the given arguments or null if it does not + * exist + * @throws SQLException + */ + public KiWiLiteral loadLiteral(boolean isGeo, String value, String lang, KiWiUriResource ltype) throws SQLException { + // look in cache + final KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(value, getLocale(lang), ltype)); + if (element != null) { + return element; + } + + requireJDBCConnection(); + + // ltype not persisted + if (ltype != null && ltype.getId() < 0) { + return null; + } + + literalLock.lock(); + + try { + // otherwise prepare a query, depending on the parameters given + final PreparedStatement query; + if (lang == null && ltype == null) { + query = getPreparedStatement("load.literal_by_v"); + query.setString(1, value); + } else if (lang != null) { + query = getPreparedStatement("load.literal_by_vl"); + query.setString(1, value); + query.setString(2, lang); + } else if (ltype != null) { + query = getPreparedStatement("load.literal_by_gv");///aqui cambiar load.literal_by_gv + query.setString(1, value); + query.setLong(2, ltype.getId()); } else { // This cannot happen... throw new IllegalArgumentException("Impossible combination of lang/type in loadLiteral!"); @@ -673,12 +791,15 @@ public class KiWiConnection implements AutoCloseable { // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return (KiWiLiteral) constructNodeFromDatabase(result); } else { return null; } + } finally { + result.close(); } } finally { literalLock.unlock(); @@ -686,30 +807,33 @@ public class KiWiConnection implements AutoCloseable { } /** - * Load a literal with the date value given as argument if it exists. The method will first look in - * the node cache for cached nodes. If no cache entry is found, it will run a database query ("load.literal_by_tv") - * on the NODES table and construct a new KiWiLiteral using the values of the literal columns - * (svalue, ivalue, ...). The type of literal returned depends on the value of the ntype column. + * Load a literal with the date value given as argument if it exists. The + * method will first look in the node cache for cached nodes. If no cache + * entry is found, it will run a database query ("load.literal_by_tv") on + * the NODES table and construct a new KiWiLiteral using the values of the + * literal columns (svalue, ivalue, ...). The type of literal returned + * depends on the value of the ntype column. * <p/> - * When a node is loaded from the database, it will be added to the different caches to speed up - * subsequent requests. + * When a node is loaded from the database, it will be added to the + * different caches to speed up subsequent requests. * * @param date the date of the date literal to load - * @return a KiWiDateLiteral with the correct date, or null if it does not exist + * @return a KiWiDateLiteral with the correct date, or null if it does not + * exist * @throws SQLException */ public KiWiDateLiteral loadLiteral(DateTime date) throws SQLException { // look in cache - KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(date.withMillisOfSecond(0),Namespaces.NS_XSD + "dateTime")); - if(element != null && element instanceof KiWiDateLiteral) { - return (KiWiDateLiteral)element; + KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(date.withMillisOfSecond(0), Namespaces.NS_XSD + "dateTime")); + if (element != null && element instanceof KiWiDateLiteral) { + return (KiWiDateLiteral) element; } requireJDBCConnection(); KiWiUriResource ltype = loadUriResource(Namespaces.NS_XSD + "dateTime"); - if(ltype == null || ltype.getId() < 0) { + if (ltype == null || ltype.getId() < 0) { return null; } @@ -719,42 +843,47 @@ public class KiWiConnection implements AutoCloseable { // otherwise prepare a query, depending on the parameters given PreparedStatement query = getPreparedStatement("load.literal_by_tv"); query.setTimestamp(1, new Timestamp(date.getMillis()), calendarUTC); - query.setInt(2, date.getZone().getOffset(date)/1000); - query.setLong(3,ltype.getId()); + query.setInt(2, date.getZone().getOffset(date) / 1000); + query.setLong(3, ltype.getId()); // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return (KiWiDateLiteral) constructNodeFromDatabase(result); } else { return null; } + } finally { + result.close(); } } finally { literalLock.unlock(); } } - /** - * Load a integer literal with the long value given as argument if it exists. The method will first look in - * the node cache for cached nodes. If no cache entry is found, it will run a database query ("load.literal_by_iv") - * on the NODES table and construct a new KiWiLiteral using the values of the literal columns - * (svalue, ivalue, ...). The type of literal returned depends on the value of the ntype column. + * Load a integer literal with the long value given as argument if it + * exists. The method will first look in the node cache for cached nodes. If + * no cache entry is found, it will run a database query + * ("load.literal_by_iv") on the NODES table and construct a new KiWiLiteral + * using the values of the literal columns (svalue, ivalue, ...). The type + * of literal returned depends on the value of the ntype column. * <p/> - * When a node is loaded from the database, it will be added to the different caches to speed up - * subsequent requests. + * When a node is loaded from the database, it will be added to the + * different caches to speed up subsequent requests. * * @param value the value of the integer literal to load - * @return a KiWiIntLiteral with the correct value, or null if it does not exist + * @return a KiWiIntLiteral with the correct value, or null if it does not + * exist * @throws SQLException */ public KiWiIntLiteral loadLiteral(long value) throws SQLException { // look in cache - KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(Long.toString(value),(String)null,Namespaces.NS_XSD + "integer")); - if(element != null && element instanceof KiWiIntLiteral) { - return (KiWiIntLiteral)element; + KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(Long.toString(value), (String) null, Namespaces.NS_XSD + "integer")); + if (element != null && element instanceof KiWiIntLiteral) { + return (KiWiIntLiteral) element; } requireJDBCConnection(); @@ -762,7 +891,7 @@ public class KiWiConnection implements AutoCloseable { KiWiUriResource ltype = loadUriResource(Namespaces.NS_XSD + "integer"); // ltype not persisted - if(ltype == null || ltype.getId() < 0) { + if (ltype == null || ltype.getId() < 0) { return null; } @@ -772,17 +901,20 @@ public class KiWiConnection implements AutoCloseable { // otherwise prepare a query, depending on the parameters given PreparedStatement query = getPreparedStatement("load.literal_by_iv"); - query.setLong(1,value); - query.setLong(2,ltype.getId()); + query.setLong(1, value); + query.setLong(2, ltype.getId()); // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return (KiWiIntLiteral) constructNodeFromDatabase(result); } else { return null; } + } finally { + result.close(); } } finally { literalLock.unlock(); @@ -790,23 +922,26 @@ public class KiWiConnection implements AutoCloseable { } /** - * Load a double literal with the double value given as argument if it exists. The method will first look in - * the node cache for cached nodes. If no cache entry is found, it will run a database query ("load.literal_by_dv") - * on the NODES table and construct a new KiWiLiteral using the values of the literal columns - * (svalue, ivalue, ...). The type of literal returned depends on the value of the ntype column. + * Load a double literal with the double value given as argument if it + * exists. The method will first look in the node cache for cached nodes. If + * no cache entry is found, it will run a database query + * ("load.literal_by_dv") on the NODES table and construct a new KiWiLiteral + * using the values of the literal columns (svalue, ivalue, ...). The type + * of literal returned depends on the value of the ntype column. * <p/> - * When a node is loaded from the database, it will be added to the different caches to speed up - * subsequent requests. + * When a node is loaded from the database, it will be added to the + * different caches to speed up subsequent requests. * * @param value the value of the integer literal to load - * @return a KiWiDoubleLiteral with the correct value, or null if it does not exist + * @return a KiWiDoubleLiteral with the correct value, or null if it does + * not exist * @throws SQLException */ public KiWiDoubleLiteral loadLiteral(double value) throws SQLException { // look in cache - KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(Double.toString(value), (String)null,Namespaces.NS_XSD + "double")); - if(element != null && element instanceof KiWiDoubleLiteral) { - return (KiWiDoubleLiteral)element; + KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(Double.toString(value), (String) null, Namespaces.NS_XSD + "double")); + if (element != null && element instanceof KiWiDoubleLiteral) { + return (KiWiDoubleLiteral) element; } requireJDBCConnection(); @@ -814,7 +949,7 @@ public class KiWiConnection implements AutoCloseable { KiWiUriResource ltype = loadUriResource(Namespaces.NS_XSD + "double"); // ltype not persisted - if(ltype == null || ltype.getId() < 0) { + if (ltype == null || ltype.getId() < 0) { return null; } @@ -828,16 +963,19 @@ public class KiWiConnection implements AutoCloseable { // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls + ResultSet result = query.executeQuery(); KiWiNode kiWiNode = null; - try (ResultSet result = query.executeQuery()) { + try { if (result.next()) { return (KiWiDoubleLiteral) constructNodeFromDatabase(result); } else { return null; } } catch (RuntimeException e) { - log.error("Unable to create KiWiDoubleLiteral for node value '{}': {}", value, e.getMessage(), e); + log.error("Unable to create KiWiDoubleLiteral for node value '{}' (id={}): {}", value, kiWiNode.getId(), e.getMessage(), e); throw e; + } finally { + result.close(); } } finally { literalLock.unlock(); @@ -845,23 +983,26 @@ public class KiWiConnection implements AutoCloseable { } /** - * Load a boolean literal with the boolean value given as argument if it exists. The method will first look in - * the node cache for cached nodes. If no cache entry is found, it will run a database query ("load.literal_by_bv") - * on the NODES table and construct a new KiWiLiteral using the values of the literal columns - * (svalue, ivalue, ...). The type of literal returned depends on the value of the ntype column. + * Load a boolean literal with the boolean value given as argument if it + * exists. The method will first look in the node cache for cached nodes. If + * no cache entry is found, it will run a database query + * ("load.literal_by_bv") on the NODES table and construct a new KiWiLiteral + * using the values of the literal columns (svalue, ivalue, ...). The type + * of literal returned depends on the value of the ntype column. * <p/> - * When a node is loaded from the database, it will be added to the different caches to speed up - * subsequent requests. + * When a node is loaded from the database, it will be added to the + * different caches to speed up subsequent requests. * * @param value the value of the integer literal to load - * @return a KiWiBooleanLiteral with the correct value, or null if it does not exist + * @return a KiWiBooleanLiteral with the correct value, or null if it does + * not exist * @throws SQLException */ public KiWiBooleanLiteral loadLiteral(boolean value) throws SQLException { // look in cache - KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(Boolean.toString(value),(String)null,Namespaces.NS_XSD + "boolean")); - if(element != null && element instanceof KiWiBooleanLiteral) { - return (KiWiBooleanLiteral)element; + KiWiLiteral element = literalCache.get(LiteralCommons.createCacheKey(Boolean.toString(value), (String) null, Namespaces.NS_XSD + "boolean")); + if (element != null && element instanceof KiWiBooleanLiteral) { + return (KiWiBooleanLiteral) element; } requireJDBCConnection(); @@ -869,7 +1010,7 @@ public class KiWiConnection implements AutoCloseable { KiWiUriResource ltype = loadUriResource(Namespaces.NS_XSD + "boolean"); // ltype not persisted - if(ltype == null || ltype.getId() < 0) { + if (ltype == null || ltype.getId() < 0) { return null; } @@ -880,16 +1021,19 @@ public class KiWiConnection implements AutoCloseable { // otherwise prepare a query, depending on the parameters given PreparedStatement query = getPreparedStatement("load.literal_by_bv"); query.setBoolean(1, value); - query.setLong(2,ltype.getId()); + query.setLong(2, ltype.getId()); // run the database query and if it yields a result, construct a new node; the method call will take care of // caching the constructed node for future calls - try (ResultSet result = query.executeQuery()) { + ResultSet result = query.executeQuery(); + try { if (result.next()) { return (KiWiBooleanLiteral) constructNodeFromDatabase(result); } else { return null; } + } finally { + result.close(); } } finally { literalLock.unlock(); @@ -897,22 +1041,25 @@ public class KiWiConnection implements AutoCloseable { } /** - * Store a new node in the database. The method will retrieve a new database id for the node and update the - * passed object. Afterwards, the node data will be inserted into the database using appropriate INSERT - * statements. The caller must make sure the connection is committed and closed properly. + * Store a new node in the database. The method will retrieve a new database + * id for the node and update the passed object. Afterwards, the node data + * will be inserted into the database using appropriate INSERT statements. + * The caller must make sure the connection is committed and closed + * properly. * <p/> - * If the node already has an ID, the method will do nothing (assuming that it is already persistent) + * If the node already has an ID, the method will do nothing (assuming that + * it is already persistent) * * - * @param node the KiWiNode to store + * @param node * @throws SQLException */ public synchronized void storeNode(KiWiNode node) throws SQLException { // ensure the data type of a literal is persisted first - if(node instanceof KiWiLiteral) { - KiWiLiteral literal = (KiWiLiteral)node; - if(literal.getType() != null && literal.getType().getId() < 0) { + if (node instanceof KiWiLiteral) { + KiWiLiteral literal = (KiWiLiteral) node; + if (literal.getType() != null && literal.getType().getId() < 0) { storeNode(literal.getType()); } } @@ -920,124 +1067,180 @@ public class KiWiConnection implements AutoCloseable { requireJDBCConnection(); // retrieve a new node id and set it in the node object - if(node.getId() < 0) { + if (node.getId() < 0) { node.setId(getNextSequence()); } // distinguish the different node types and run the appropriate updates - if(node instanceof KiWiUriResource) { - KiWiUriResource uriResource = (KiWiUriResource)node; + if (node instanceof KiWiUriResource) { + KiWiUriResource uriResource = (KiWiUriResource) node; PreparedStatement insertNode = getPreparedStatement("store.uri"); - insertNode.setLong(1,node.getId()); - insertNode.setString(2,uriResource.stringValue()); + insertNode.setLong(1, node.getId()); + insertNode.setString(2, uriResource.stringValue()); insertNode.setTimestamp(3, new Timestamp(uriResource.getCreated().getTime()), calendarUTC); insertNode.executeUpdate(); - } else if(node instanceof KiWiAnonResource) { - KiWiAnonResource anonResource = (KiWiAnonResource)node; + } else if (node instanceof KiWiAnonResource) { + KiWiAnonResource anonResource = (KiWiAnonResource) node; PreparedStatement insertNode = getPreparedStatement("store.bnode"); - insertNode.setLong(1,node.getId()); - insertNode.setString(2,anonResource.stringValue()); + insertNode.setLong(1, node.getId()); + insertNode.setString(2, anonResource.stringValue()); insertNode.setTimestamp(3, new Timestamp(anonResource.getCreated().getTime()), calendarUTC); insertNode.executeUpdate(); - } else if(node instanceof KiWiDateLiteral) { - KiWiDateLiteral dateLiteral = (KiWiDateLiteral)node; + } else if (node instanceof KiWiDateLiteral) { + KiWiDateLiteral dateLiteral = (KiWiDateLiteral) node; PreparedStatement insertNode = getPreparedStatement("store.tliteral"); - insertNode.setLong(1,node.getId()); + insertNode.setLong(1, node.getId()); insertNode.setString(2, dateLiteral.stringValue()); insertNode.setTimestamp(3, new Timestamp(dateLiteral.getDateContent().getMillis()), calendarUTC); - insertNode.setInt(4, dateLiteral.getDateContent().getZone().getOffset(dateLiteral.getDateContent())/1000); - if(dateLiteral.getType() != null) - insertNode.setLong(5,dateLiteral.getType().getId()); - else + insertNode.setInt(4, dateLiteral.getDateContent().getZone().getOffset(dateLiteral.getDateContent()) / 1000); + if (dateLiteral.getType() != null) { + insertNode.setLong(5, dateLiteral.getType().getId()); + } else { throw new IllegalStateException("a date literal must have a datatype"); + } insertNode.setTimestamp(6, new Timestamp(dateLiteral.getCreated().getTime()), calendarUTC); insertNode.executeUpdate(); - } else if(node instanceof KiWiIntLiteral) { - KiWiIntLiteral intLiteral = (KiWiIntLiteral)node; + } else if (node instanceof KiWiIntLiteral) { + KiWiIntLiteral intLiteral = (KiWiIntLiteral) node; PreparedStatement insertNode = getPreparedStatement("store.iliteral"); - insertNode.setLong(1,node.getId()); + insertNode.setLong(1, node.getId()); insertNode.setString(2, intLiteral.getContent()); insertNode.setDouble(3, intLiteral.getDoubleContent()); insertNode.setLong(4, intLiteral.getIntContent()); - if(intLiteral.getType() != null) - insertNode.setLong(5,intLiteral.getType().getId()); - else + if (intLiteral.getType() != null) { + insertNode.setLong(5, intLiteral.getType().getId()); + } else { throw new IllegalStateException("an integer literal must have a datatype"); + } insertNode.setTimestamp(6, new Timestamp(intLiteral.getCreated().getTime()), calendarUTC); insertNode.executeUpdate(); - } else if(node instanceof KiWiDoubleLiteral) { - KiWiDoubleLiteral doubleLiteral = (KiWiDoubleLiteral)node; + } else if (node instanceof KiWiDoubleLiteral) { + KiWiDoubleLiteral doubleLiteral = (KiWiDoubleLiteral) node; PreparedStatement insertNode = getPreparedStatement("store.dliteral"); insertNode.setLong(1, node.getId()); insertNode.setString(2, doubleLiteral.getContent()); insertNode.setDouble(3, doubleLiteral.getDoubleContent()); - if(doubleLiteral.getType() != null) - insertNode.setLong(4,doubleLiteral.getType().getId()); - else + if (doubleLiteral.getType() != null) { + insertNode.setLong(4, doubleLiteral.getType().getId()); + } else { throw new IllegalStateException("a double literal must have a datatype"); + } insertNode.setTimestamp(5, new Timestamp(doubleLiteral.getCreated().getTime()), calendarUTC); insertNode.executeUpdate(); - } else if(node instanceof KiWiBooleanLiteral) { - KiWiBooleanLiteral booleanLiteral = (KiWiBooleanLiteral)node; + } else if (node instanceof KiWiBooleanLiteral) { + KiWiBooleanLiteral booleanLiteral = (KiWiBooleanLiteral) node; PreparedStatement insertNode = getPreparedStatement("store.bliteral"); - insertNode.setLong(1,node.getId()); + insertNode.setLong(1, node.getId()); insertNode.setString(2, booleanLiteral.getContent()); insertNode.setBoolean(3, booleanLiteral.booleanValue()); - if(booleanLiteral.getType() != null) - insertNode.setLong(4,booleanLiteral.getType().getId()); - else + if (booleanLiteral.getType() != null) { + insertNode.setLong(4, booleanLiteral.getType().getId()); + } else { throw new IllegalStateException("a boolean literal must have a datatype"); + } insertNode.setTimestamp(5, new Timestamp(booleanLiteral.getCreated().getTime()), calendarUTC); insertNode.executeUpdate(); - } else if(node instanceof KiWiStringLiteral) { - KiWiStringLiteral stringLiteral = (KiWiStringLiteral)node; + } else if (node instanceof KiWiGeometryLiteral) { + KiWiGeometryLiteral geoLiteral = (KiWiGeometryLiteral) node; + Double dbl_value = null; + Long lng_value = null; + String gvalue = ""; + if (geoLiteral.getContent().length() < 64 && NumberUtils.isNumber(geoLiteral.getContent())) { + try { + dbl_value = Double.parseDouble(geoLiteral.getContent()); + lng_value = Long.parseLong(geoLiteral.getContent()); + } catch (NumberFormatException ex) { + // ignore, keep NaN + } + } + + if (geoLiteral.getContent().contains("POINT")) { + gvalue = geoLiteral.getContent().substring(geoLiteral.getContent().indexOf("POINT")); + } + if (geoLiteral.getContent().contains("MULTILINESTRING")) { + gvalue = geoLiteral.getContent().substring(geoLiteral.getContent().indexOf("MULTILINESTRING")); + } + if (geoLiteral.getContent().contains("MULTIPOLYGON")) { + gvalue = geoLiteral.getContent().substring(geoLiteral.getContent().indexOf("MULTIPOLYGON")); + } + + PreparedStatement insertNode = getPreparedStatement("store.gliteral"); + insertNode.setLong(1, node.getId()); + insertNode.setString(2, "Geometry Resource"); + insertNode.setString(8, gvalue); + if (dbl_value != null) { + insertNode.setDouble(3, dbl_value); + } else { + insertNode.setObject(3, null); + } + if (lng_value != null) { + insertNode.setLong(4, lng_value); + } else { + insertNode.setObject(4, null); + } + + if (geoLiteral.getLocale() != null) { + insertNode.setString(5, geoLiteral.getLocale().getLanguage().toLowerCase()); + } else { + insertNode.setObject(5, null); + } + if (geoLiteral.getType() != null) { + insertNode.setLong(6, geoLiteral.getType().getId()); + } else { + insertNode.setObject(6, null); + } + insertNode.setTimestamp(7, new Timestamp(geoLiteral.getCreated().getTime()), calendarUTC); + + insertNode.executeUpdate(); + } else if (node instanceof KiWiStringLiteral) { + KiWiStringLiteral stringLiteral = (KiWiStringLiteral) node; Double dbl_value = null; - Long lng_value = null; - if(stringLiteral.getContent().length() < 64 && NumberUtils.isNumber(stringLiteral.getContent())) + Long lng_value = null; + if (stringLiteral.getContent().length() < 64 && NumberUtils.isNumber(stringLiteral.getContent())) { try { dbl_value = Double.parseDouble(stringLiteral.getContent()); lng_value = Long.parseLong(stringLiteral.getContent()); } catch (NumberFormatException ex) { // ignore, keep NaN } - + } PreparedStatement insertNode = getPreparedStatement("store.sliteral"); - insertNode.setLong(1,node.getId()); + insertNode.setLong(1, node.getId()); insertNode.setString(2, stringLiteral.getContent()); - if(dbl_value != null) { + if (dbl_value != null) { insertNode.setDouble(3, dbl_value); } else { insertNode.setObject(3, null); } - if(lng_value != null) { + if (lng_value != null) { insertNode.setLong(4, lng_value); } else { insertNode.setObject(4, null); } - if(stringLiteral.getLocale() != null) { + if (stringLiteral.getLocale() != null) { insertNode.setString(5, stringLiteral.getLocale().getLanguage().toLowerCase()); } else { insertNode.setObject(5, null); } - if(stringLiteral.getType() != null) { - insertNode.setLong(6,stringLiteral.getType().getId()); + if (stringLiteral.getType() != null) { + insertNode.setLong(6, stringLiteral.getType().getId()); } else { insertNode.setObject(6, null); } @@ -1052,11 +1255,15 @@ public class KiWiConnection implements AutoCloseable { } /** - * Store a triple in the database. This method assumes that all nodes used by the triple are already persisted. + * Store a triple in the database. This method assumes that all nodes used + * by the triple are already persisted. * - * @param triple the triple to store + * @param triple the triple to store * @throws SQLException - * @throws NullPointerException in case the subject, predicate, object or context have not been persisted + * @throws NullPointerException in case the subject, predicate, object or + * context have not been persisted + * @return true in case the update added a new triple to the database, false + * in case the triple already existed */ public synchronized void storeTriple(final KiWiTriple triple) throws SQLException { // mutual exclusion: prevent parallel adding and removing of the same triple @@ -1064,11 +1271,11 @@ public class KiWiConnection implements AutoCloseable { requireJDBCConnection(); - if(triple.getId() < 0) { + if (triple.getId() < 0) { triple.setId(getNextSequence()); } - if(deletedStatementsLog.mightContain(triple.getId())) { + if (deletedStatementsLog.mightContain(triple.getId())) { // this is a hack for a concurrency problem that may occur in case the triple is removed in the // transaction and then added again; in these cases the createStatement method might return // an expired state of the triple because it uses its own database connection @@ -1078,23 +1285,22 @@ public class KiWiConnection implements AutoCloseable { } else { - if(batchCommit) { + if (batchCommit) { commitLock.lock(); try { cacheTriple(triple); tripleBatch.add(triple); - if(tripleBatch.size() >= batchSize) { + if (tripleBatch.size() >= batchSize) { flushBatch(); } } finally { commitLock.unlock(); } - } else { + } else { Preconditions.checkNotNull(triple.getSubject().getId()); Preconditions.checkNotNull(triple.getPredicate().getId()); Preconditions.checkNotNull(triple.getObject().getId()); - try { RetryExecution<Boolean> execution = new RetryExecution<>("STORE"); execution.setUseSavepoint(true); @@ -1102,16 +1308,16 @@ public class KiWiConnection implements AutoCloseable { @Override public Boolean run() throws SQLException { PreparedStatement insertTriple = getPreparedStatement("store.triple"); - insertTriple.setLong(1,triple.getId()); - insertTriple.setLong(2,triple.getSubject().getId()); - insertTriple.setLong(3,triple.getPredicate().getId()); - insertTriple.setLong(4,triple.getObject().getId()); - if(triple.getContext() != null) { - insertTriple.setLong(5,triple.getContext().getId()); + insertTriple.setLong(1, triple.getId()); + insertTriple.setLong(2, triple.getSubject().getId()); + insertTriple.setLong(3, triple.getPredicate().getId()); + insertTriple.setLong(4, triple.getObject().getId()); + if (triple.getContext() != null) { + insertTriple.setLong(5, triple.getContext().getId()); } else { insertTriple.setNull(5, Types.BIGINT); } - insertTriple.setBoolean(6,triple.isInferred()); + insertTriple.setBoolean(6, triple.isInferred()); insertTriple.setTimestamp(7, new Timestamp(triple.getCreated().getTime())); int count = insertTriple.executeUpdate(); @@ -1121,9 +1327,9 @@ public class KiWiConnection implements AutoCloseable { } }); - } catch(SQLException ex) { - if("HYT00".equals(ex.getSQLState())) { // H2 table locking timeout - throw new ConcurrentModificationException("the same triple was modified in concurrent transactions (triple="+triple+")"); + } catch (SQLException ex) { + if ("HYT00".equals(ex.getSQLState())) { // H2 table locking timeout + throw new ConcurrentModificationException("the same triple was modified in concurrent transactions (triple=" + triple + ")"); } else { throw ex; } @@ -1134,19 +1340,20 @@ public class KiWiConnection implements AutoCloseable { } /** - * Return the identifier of the triple with the given subject, predicate, object and context, or null if this - * triple does not exist. Used for quick existance checks of triples. + * Return the identifier of the triple with the given subject, predicate, + * object and context, or null if this triple does not exist. Used for quick + * existance checks of triples. * - * @param subject the subject of the triple - * @param predicate the predicate of the triple - * @param object the object of the triple - * @param context the context of the triple - * @return the database-id of the triple or {@code -1} if it does not exist. + * @param subject + * @param predicate + * @param object + * @param context + * @return */ public synchronized long getTripleId(final KiWiResource subject, final KiWiUriResource predicate, final KiWiNode object, final KiWiResource context) throws SQLException { - if(tripleBatch != null && tripleBatch.size() > 0) { - Collection<KiWiTriple> batched = tripleBatch.listTriples(subject,predicate,object,context, false); - if(batched.size() > 0) { + if (tripleBatch != null && tripleBatch.size() > 0) { + Collection<KiWiTriple> batched = tripleBatch.listTriples(subject, predicate, object, context, false); + if (batched.size() > 0) { return batched.iterator().next().getId(); } } @@ -1156,30 +1363,34 @@ public class KiWiConnection implements AutoCloseable { loadTripleId.setLong(1, subject.getId()); loadTripleId.setLong(2, predicate.getId()); loadTripleId.setLong(3, object.getId()); - if(context != null) { + if (context != null) { loadTripleId.setLong(4, context.getId()); } else { loadTripleId.setNull(4, Types.BIGINT); } - try (ResultSet result = loadTripleId.executeQuery()) { + ResultSet result = loadTripleId.executeQuery(); + try { if (result.next()) { return result.getLong(1); } else { return -1L; } + } finally { + result.close(); } } /** - * Mark the triple passed as argument as deleted, setting the "deleted" flag to true and - * updating the timestamp value of "deletedAt". + * Mark the triple passed as argument as deleted, setting the "deleted" flag + * to true and updating the timestamp value of "deletedAt". * <p/> - * The triple remains in the database, because other entities might still reference it (e.g. a version). - * Use the method cleanupTriples() to fully remove all deleted triples without references. + * The triple remains in the database, because other entities might still + * reference it (e.g. a version). Use the method cleanupTriples() to fully + * remove all deleted triples without references. * - * @param triple the KiWiTriple to delete + * @param triple */ public void deleteTriple(final KiWiTriple triple) throws SQLException { requireJDBCConnection(); @@ -1225,7 +1436,6 @@ public class KiWiConnection implements AutoCloseable { } deletedStatementsLog.put(triple.getId()); - } } removeCachedTriple(triple); @@ -1238,14 +1448,17 @@ public class KiWiConnection implements AutoCloseable { } /** - * Mark all triples contained in the context passed as argument as deleted, setting the "deleted" flag to true and - * updating the timestamp value of "deletedAt". + * Mark all triples contained in the context passed as argument as deleted, + * setting the "deleted" flag to true and updating the timestamp value of + * "deletedAt". * <p/> - * The triple remains in the database, because other entities might still reference it (e.g. a version). - * Use the method cleanupTriples() to fully remove all deleted triples without references. + * The triple remains in the database, because other entities might still + * reference it (e.g. a version). Use the method cleanupTriples() to fully + * remove all deleted triples without references. * <p/> - * Warning: this method skips some concurrency and transaction safeguards for performance and therefore should - * only be called if run in an isolated transaction! + * Warning: this method skips some concurrency and transaction safeguards + * for performance and therefore should only be called if run in an isolated + * transaction! * * @param ctx resource identifying the context to be deleted */ @@ -1276,7 +1489,7 @@ public class KiWiConnection implements AutoCloseable { // deletedStatementsLog.put(triple.getId()); } else { // delete all triples from triple batch with a matching context - for (Iterator<KiWiTriple> it = tripleBatch.iterator(); it.hasNext(); ) { + for (Iterator<KiWiTriple> it = tripleBatch.iterator(); it.hasNext();) { if (it.next().getContext().equals(ctx)) { it.remove(); } @@ -1295,7 +1508,6 @@ public class KiWiConnection implements AutoCloseable { } //deletedStatementsLog.put(triple.getId()); - } } //removeCachedTriple(triple); @@ -1310,14 +1522,16 @@ public class KiWiConnection implements AutoCloseable { } /** - * Mark all triples contained in the triple store as deleted, setting the "deleted" flag to true and - * updating the timestamp value of "deletedAt". + * Mark all triples contained in the triple store as deleted, setting the + * "deleted" flag to true and updating the timestamp value of "deletedAt". * <p/> - * The triple remains in the database, because other entities might still reference it (e.g. a version). - * Use the method cleanupTriples() to fully remove all deleted triples without references. + * The triple remains in the database, because other entities might still + * reference it (e.g. a version). Use the method cleanupTriples() to fully + * remove all deleted triples without references. * <p/> - * Warning: this method skips some concurrency and transaction safeguards for performance and therefore should - * only be called if run in an isolated transaction! + * Warning: this method skips some concurrency and transaction safeguards + * for performance and therefore should only be called if run in an isolated + * transaction! * */ public void deleteAll() throws SQLException { @@ -1357,7 +1571,6 @@ public class KiWiConnection implements AutoCloseable { } //deletedStatementsLog.put(triple.getId()); - } //removeCachedTriple(triple); @@ -1370,19 +1583,18 @@ public class KiWiConnection implements AutoCloseable { } - /** - * Mark the triple passed as argument as not deleted, setting the "deleted" flag to false and - * clearing the timestamp value of "deletedAt". + * Mark the triple passed as argument as not deleted, setting the "deleted" + * flag to false and clearing the timestamp value of "deletedAt". * <p/> - * Note that this operation should only be called if the triple was deleted before in the same - * transaction! + * Note that this operation should only be called if the triple was deleted + * before in the same transaction! * - * @param triple the KiWiTriple to restore + * @param triple */ public void undeleteTriple(KiWiTriple triple) throws SQLException { - if(triple.getId() < 0) { - log.warn("attempting to undelete non-persistent triple: {}",triple); + if (triple.getId() < 0) { + log.warn("attempting to undelete non-persistent triple: {}", triple); return; } @@ -1393,15 +1605,15 @@ public class KiWiConnection implements AutoCloseable { triple.setDeletedAt(null); synchronized (triple) { - if(!triple.isDeleted()) { - log.warn("attempting to undelete triple that was not deleted: {}",triple); + if (!triple.isDeleted()) { + log.warn("attempting to undelete triple that was not deleted: {}", triple); } PreparedStatement undeleteTriple = getPreparedStatement("undelete.triple"); undeleteTriple.setLong(1, triple.getId()); undeleteTriple.executeUpdate(); - if(!persistence.getConfiguration().isClustered()) { + if (!persistence.getConfiguration().isClustered()) { cacheTriple(triple); } } @@ -1410,7 +1622,8 @@ public class KiWiConnection implements AutoCloseable { /** * List all contexts used in this triple store. See query.contexts . - * @return ClosableIteration of contexts + * + * @return * @throws SQLException */ public CloseableIteration<KiWiResource, SQLException> listContexts() throws SQLException { @@ -1420,30 +1633,29 @@ public class KiWiConnection implements AutoCloseable { final ResultSet result = queryContexts.executeQuery(); - if(tripleBatch != null && tripleBatch.size() > 0) { - return new DistinctIteration<>( - new UnionIteration<>( - new ConvertingIteration<Resource,KiWiResource,SQLException>(new IteratorIteration<Resource, SQLException>(tripleBatch.listContextIDs().iterator())) { + if (tripleBatch != null && tripleBatch.size() > 0) { + return new DistinctIteration<KiWiResource, SQLException>( + new UnionIteration<KiWiResource, SQLException>( + new ConvertingIteration<Resource, KiWiResource, SQLException>(new IteratorIteration<Resource, SQLException>(tripleBatch.listContextIDs().iterator())) { @Override protected KiWiResource convert(Resource sourceObject) throws SQLException { - return (KiWiResource)sourceObject; + return (KiWiResource) sourceObject; } }, - new ResultSetIteration<>(result, new ResultTransformerFunction<KiWiResource>() { + new ResultSetIteration<KiWiResource>(result, new ResultTransformerFunction<KiWiResource>() { @Override public KiWiResource apply(ResultSet row) throws SQLException { - return (KiWiResource)loadNodeById(result.getLong("context")); + return (KiWiResource) loadNodeById(result.getLong("context")); } }) ) ); - } else { - return new ResultSetIteration<>(result, new ResultTransformerFunction<KiWiResource>() { + return new ResultSetIteration<KiWiResource>(result, new ResultTransformerFunction<KiWiResource>() { @Override public KiWiResource apply(ResultSet row) throws SQLException { - return (KiWiResource)loadNodeById(result.getLong("context")); + return (KiWiResource) loadNodeById(result.getLong("context")); } }); } @@ -1452,6 +1664,7 @@ public class KiWiConnection implements AutoCloseable { /** * List all contexts used in this triple store. See query.contexts . + * * @return * @throws SQLException */ @@ -1462,10 +1675,10 @@ public class KiWiConnection implements AutoCloseable { final ResultSet result = queryContexts.executeQuery(); - return new ResultSetIteration<>(result, new ResultTransformerFunction<KiWiResource>() { + return new ResultSetIteration<KiWiResource>(result, new ResultTransformerFunction<KiWiResource>() { @Override public KiWiResource apply(ResultSet row) throws SQLException { - return (KiWiResource)constructNodeFromDatabase(row); + return (KiWiResource) constructNodeFromDatabase(row); } }); @@ -1473,6 +1686,7 @@ public class KiWiConnection implements AutoCloseable { /** * List all contexts used in this triple store. See query.contexts . + * * @return * @throws SQLException */ @@ -1484,16 +1698,15 @@ public class KiWiConnection implements AutoCloseable { final ResultSet result = queryContexts.executeQuery(); - return new ResultSetIteration<>(result, new ResultTransformerFunction<KiWiUriResource>() { + return new ResultSetIteration<KiWiUriResource>(result, new ResultTransformerFunction<KiWiUriResource>() { @Override public KiWiUriResource apply(ResultSet row) throws SQLException { - return (KiWiUriResource)constructNodeFromDatabase(row); + return (KiWiUriResource) constructNodeFromDatabase(row); } }); } - public CloseableIteration<KiWiNamespace, SQLException> listNamespaces() throws SQLException { requireJDBCConnection(); @@ -1501,7 +1714,7 @@ public class KiWiConnection implements AutoCloseable { final ResultSet result = queryContexts.executeQuery(); - return new ResultSetIteration<>(result, new ResultTransformerFunction<KiWiNamespace>() { + return new ResultSetIteration<KiWiNamespace>(result, new ResultTransformerFunction<KiWiNamespace>() { @Override public KiWiNamespace apply(ResultSet input) throws SQLException { return constructNamespaceFromDatabase(result); @@ -1509,93 +1722,96 @@ public class KiWiConnection implements AutoCloseable { }); } - /** - * Return a Sesame RepositoryResult of statements according to the query pattern given in the arguments. Each of - * the parameters subject, predicate, object and context may be null, indicating a wildcard query. If the boolean - * parameter "inferred" is set to true, the result will also include inferred triples, if it is set to false only - * base triples. + * Return a Sesame RepositoryResult of statements according to the query + * pattern given in the arguments. Each of the parameters subject, + * predicate, object and context may be null, indicating a wildcard query. + * If the boolean parameter "inferred" is set to true, the result will also + * include inferred triples, if it is set to false only base triples. * <p/> - * The RepositoryResult holds a direct connection to the database and needs to be closed properly, or otherwise - * the system might run out of resources. The returned RepositoryResult will try its best to clean up when the - * iteration has completed or the garbage collector calls the finalize() method, but this can take longer than - * necessary. + * The RepositoryResult holds a direct connection to the database and needs + * to be closed properly, or otherwise the system might run out of + * resources. The returned RepositoryResult will try its best to clean up + * when the iteration has completed or the garbage collector calls the + * finalize() method, but this can take longer than necessary. * * - * @param subject the subject to query for, or null for a wildcard query - * @param predicate the predicate to query for, or null for a wildcard query - * @param object the object to query for, or null for a wildcard query - * @param context the context to query for, or null for a wildcard query - * @param inferred if true, the result will also contain triples inferred by the reasoner, if false not - * @param wildcardContext if true, a null context will be interpreted as a wildcard, if false, a null context will be interpreted as "no context" - * @return a new RepositoryResult with a direct connection to the database; the result should be properly closed - * by the caller + * @param subject the subject to query for, or null for a wildcard query + * @param predicate the predicate to query for, or null for a wildcard query + * @param object the object to query for, or null for a wildcard query + * @param context the context to query for, or null for a wildcard query + * @param inferred if true, the result will also contain triples inferred by + * the reasoner, if false not + * @param wildcardContext if true, a null context will be interpreted as a + * wildcard, if false, a null context will be interpreted as "no context" + * @return a new RepositoryResult with a direct connection to the database; + * the result should be properly closed by the caller */ public RepositoryResult<Statement> listTriples(final KiWiResource subject, final KiWiUriResource predicate, final KiWiNode object, final KiWiResource context, final boolean inferred, final boolean wildcardContext) throws SQLException { - - if(tripleBatch != null && tripleBatch.size() > 0) { + if (tripleBatch != null && tripleBatch.size() > 0) { synchronized (tripleBatch) { - return new RepositoryRes
<TRUNCATED>
