/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed 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
*
* 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.
* !#
*/
package net.ontopia.persistence.proxy;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import net.ontopia.persistence.query.jdo.JDOQuery;
import net.ontopia.persistence.query.sql.DetachedQueryIF;
import net.ontopia.persistence.query.sql.EqualsSQLOptimizer;
import net.ontopia.persistence.query.sql.GenericSQLGenerator;
import net.ontopia.persistence.query.sql.RDBMSMatrixQuery;
import net.ontopia.persistence.query.sql.RDBMSQuery;
import net.ontopia.persistence.query.sql.RedundantTablesSQLOptimizer;
import net.ontopia.persistence.query.sql.SQLBuilder;
import net.ontopia.persistence.query.sql.SQLGeneratorIF;
import net.ontopia.persistence.query.sql.SQLQuery;
import net.ontopia.persistence.query.sql.SQLStatementIF;
import net.ontopia.topicmaps.entry.TopicMapReferenceIF;
import net.ontopia.topicmaps.impl.rdbms.RDBMSTopicMapReference;
import net.ontopia.utils.OntopiaRuntimeException;
import net.ontopia.utils.PropertyUtils;
import net.ontopia.utils.StreamUtils;
import net.ontopia.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* INTERNAL: A storage definition implementation for relational
* databases.
*/
public class RDBMSStorage implements StorageIF {
// Define a logging category.
static Logger log = LoggerFactory.getLogger(RDBMSStorage.class.getName());
static final Set<String> known_properties;
static {
known_properties = new HashSet<String>();
known_properties.add("net.ontopia.topicmaps.impl.rdbms.BatchUpdates");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.identitymap.lru");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.local.debug");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.rolesbytype.lru");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.rolesbytype2.lru");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.shared");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.shared.debug");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.shared.identitymap.lru");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.subjectidentity.srcloc.lru");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.subjectidentity.subind.lru");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cache.subjectidentity.subloc.lru");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cluster.id");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Cluster.properties");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.ConnectionPool");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.ConnectionPool.JNDIDataSource");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.ConnectionPool.MaximumSize");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.ConnectionPool.MinimumSize");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.ConnectionPool.PoolStatements");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.ConnectionPool.SoftMaximum");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.ConnectionPool.UserTimeout");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.ConnectionPool.ValidationQuery");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.ConnectionString");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Database");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.DriverClass");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.GlobalEntry");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.HighLowKeyGenerator.SelectSuffix");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.KeyBlockSize");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.MappingFile");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Password");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.Platforms");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.QueriesFile");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.StorePool.MaximumSize");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.StorePool.MinimumSize");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.StorePool.SoftMaximum");
known_properties.add("net.ontopia.topicmaps.impl.rdbms.UserName");
known_properties.add("net.ontopia.topicmaps.query.core.QueryProcessorIF");
known_properties.add("net.ontopia.topicmaps.query.core.QueryProcessorIF.locale");
known_properties.add("net.ontopia.topicmaps.query.impl.rdbms.ValuePredicate.function");
known_properties.add("net.ontopia.topicmaps.query.impl.rdbms.ValuePredicate.function.type");
known_properties.add("net.ontopia.topicmaps.query.impl.rdbms.ValueLikePredicate.function");
known_properties.add("net.ontopia.topicmaps.query.impl.rdbms.ValueLikePredicate.function.type");
known_properties.add("net.ontopia.infoset.fulltext.impl.rdbms.RDBMSSearcher.type");
}
private Map<String, String> properties;
private RDBMSMapping mapping;
private QueryDeclarations queries;
private StorageCacheIF scache;
private StorageAccessIF saccess;
private ConnectionFactoryIF rw_connfactory;
private ConnectionFactoryIF ro_connfactory;
private KeyGeneratorIF keygen;
private String database;
private String[] platforms;
private int access_counter;
private SQLBuilder sqlbuilder;
private SQLGeneratorIF sqlgen;
private CachesIF caches;
private ClusterIF cluster;
private final Set<AbstractTransaction> transactions = Collections.newSetFromMap(new WeakHashMap<AbstractTransaction, Boolean>());
/**
* INTERNAL: Creates a storage definition which gets its settings
* from system variables.
*/
public RDBMSStorage() throws IOException {
this(System.getProperty("net.ontopia.topicmaps.impl.rdbms.PropertyFile"));
}
/**
* INTERNAL: Creates a storage definition that reads its settings
* from the specified property file.
*/
public RDBMSStorage(String propfile) throws IOException {
// TODO: attempt to load props from CLASSPATH (file: oks.rdbms.props)
if (propfile == null)
throw new NullPointerException("Property file cannot be null. Please set the 'net.ontopia.topicmaps.impl.rdbms.PropertyFile' property.");
// Load properties from file
InputStream istream = StreamUtils.getInputStream(propfile);
if (istream == null)
throw new OntopiaRuntimeException("Property file '" + propfile + "' was not found.");
if (log.isDebugEnabled())
log.info("Loading properties file from: " + propfile);
init(PropertyUtils.toMap(PropertyUtils.loadProperties(istream)));
}
/**
* INTERNAL: Creates a storage definition that reads its settings
* from the specified properties.
*
* @since 1.2.4
*/
public RDBMSStorage(Map<String, String> properties) throws IOException {
// Pass on user current directory
init(properties);
}
protected InputStream getInputStream(String property, String filename)
throws IOException {
String _filename = properties.get(property);
if (_filename == null) {
_filename = "classpath:net/ontopia/topicmaps/impl/rdbms/config/" + filename;
} else {
log.debug(filename + ": using file '" + _filename + "'");
}
return StreamUtils.getInputStream(_filename);
}
/**
* INTERNAL: Method shared by constructors to properly initialize members.
*/
protected void init(Map<String, String> properties) throws IOException {
// Set storage properties
this.properties = properties;
// Get mapping.xml file
InputStream mstream = getInputStream("net.ontopia.topicmaps.impl.rdbms.MappingFile",
"mapping.xml");
if (mstream == null)
throw new OntopiaRuntimeException("Object-relational mapping file 'mapping.xml' cannot be found.");
// Get queries.xml file
InputStream qstream = getInputStream("net.ontopia.topicmaps.impl.rdbms.QueriesFile",
"queries.xml");
if (qstream == null)
throw new OntopiaRuntimeException("Built-in queries file 'queries.xml' cannot be found.");
// Read configuration files
this.mapping = new RDBMSMapping(new ObjectRelationalMapping(mstream));
this.queries = new QueryDeclarations(qstream);
// Set up connection factory
String cptype = properties.get("net.ontopia.topicmaps.impl.rdbms.ConnectionPool");
if (cptype == null || "true".equals(cptype) || "yes".equals(cptype) || "dbcp".equals(cptype)) {
log.debug("Using DBCP connection pool.");
this.rw_connfactory = new DBCPConnectionFactory(properties, false); // default
this.ro_connfactory = new DBCPConnectionFactory(properties, true); // default
} else if ("jndi".equals(cptype)) {
log.debug("Using JNDI connection pool.");
this.rw_connfactory = new JNDIConnectionFactory(properties);
this.ro_connfactory = new JNDIConnectionFactory(properties);
} else {
log.debug("Using default connection factory (i.e. no pool).");
this.rw_connfactory = new DefaultConnectionFactory(properties, false);
this.ro_connfactory = new DefaultConnectionFactory(properties, true);
}
// Get database
this.database = getProperty("net.ontopia.topicmaps.impl.rdbms.Database");
if (this.database == null)
throw new OntopiaRuntimeException("The property 'net.ontopia.topicmaps.impl.rdbms.Database' is not set.");
// Get platforms
String _platforms = getProperty("net.ontopia.topicmaps.impl.rdbms.Platforms");
if (_platforms == null) {
if (database.equals("oracle") || database.equals("oracle8") || database.equals("oracle8i"))
_platforms = "oracle8,oracle,generic";
else if (database.equals("oracle9") || database.equals("oracle9i"))
_platforms = "oracle9i,oracle,generic";
else if (database.equals("oracle10") || database.equals("oracle10g"))
_platforms = "oracle10g,oracle,generic";
else if (database.equals("postgresql"))
_platforms = "postgresql,generic";
else if (database.equals("mysql"))
_platforms = "mysql,generic";
else if (database.equals("db2"))
_platforms = "db2,generic";
else if (database.equals("sapdb"))
_platforms = "sabdb,generic";
else if (database.equals("firebird"))
_platforms = "firebird,generic";
else if (database.equals("derby"))
_platforms = "derby,generic";
else if (database.equals("sqlserver"))
_platforms = "sqlserver,generic";
else if (database.equals("h2"))
_platforms = "h2,generic";
else if (database.equals("generic"))
_platforms = "generic";
else
throw new OntopiaRuntimeException("The datatype type is unknown and the property 'net.ontopia.topicmaps.impl.rdbms.Platforms' is not set.");
}
this.platforms = StringUtils.split(_platforms, ",");
// Initialize key generator
String kbprop = getProperty("net.ontopia.topicmaps.impl.rdbms.KeyBlockSize", "200");
String global_entry = getProperty("net.ontopia.topicmaps.impl.rdbms.GlobalEntry", "<GLOBAL>");
// Initialize key generator
// TODO: Remove dependency on hardcoded key generator schema names.
this.keygen = new HighLowKeyGenerator(this.rw_connfactory,
"TM_ADMIN_SEQUENCE", "seq_name", "seq_count",
global_entry, Integer.parseInt(kbprop), database, properties);
// Create query builders
this.sqlbuilder = new SQLBuilder(getMapping(), PropertyUtils.isTrue(getProperty("net.ontopia.persistence.query.sql.SQLBuilder.debug")));
this.sqlgen = GenericSQLGenerator.getSQLGenerator(getPlatforms(), properties);
// Register jdbcspy driver
try {
Class.forName("net.ontopia.persistence.jdbcspy.SpyDriver");
} catch (ClassNotFoundException e) {
// ignore if not exists
}
// initialize cluster
String clusterId = getProperty("net.ontopia.topicmaps.impl.rdbms.Cluster.id");
if (clusterId != null) {
if (clusterId.startsWith("jgroups:")) {
String clusterProps = getProperty("net.ontopia.topicmaps.impl.rdbms.Cluster.properties");
this.cluster = new JGroupsCluster(clusterId, clusterProps, this);
this.caches = new JGroupsCaches(cluster);
} else {
throw new OntopiaRuntimeException("Not able to figure out cluster type from cluster id: " + clusterId);
}
}
if (this.caches == null)
this.caches = new DefaultCaches();
// initialize shared cache
if (PropertyUtils.isTrue(getProperty("net.ontopia.topicmaps.impl.rdbms.Cache.shared"), true)) {
// default shared cache
this.scache = new SharedCache(this, caches.<IdentityIF, CacheEntry>createDataCache());
((SharedCache)this.scache).setCluster(cluster);
// instrument shared cache
int dinterval = PropertyUtils.getInt(getProperty("net.ontopia.topicmaps.impl.rdbms.Cache.shared.debug"), -1);
if (dinterval > 0) {
log.info("Instrumenting shared cache.");
this.scache = new StatisticsCache("scache", scache, dinterval);
}
} else if (this.cluster != null) {
log.warn("");
log.warn("");
log.warn(" /vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\\");
log.warn(" > <");
log.warn(" > Joining a cluster without a shared storage cache will not work ! <");
log.warn(" > <");
log.warn(" > Don't set property 'net.ontopia.topicmaps.impl.rdbms.Cache.shared' to 'false' <");
log.warn(" > in combination with property 'net.ontopia.topicmaps.impl.rdbms.Cluster.id'. <");
log.warn(" > <");
log.warn(" \\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^/");
log.warn("");
log.warn("");
}
// join cluster
if (this.cluster != null)
this.cluster.join();
}
public RDBMSMapping getMapping() {
return mapping;
}
public QueryDeclarations getQueryDeclarations() {
return queries;
}
public IdentityIF generateIdentity(Class<?> type) {
return keygen.generateKey(type);
}
public Map<String, String> getProperties() {
return properties;
}
public String getProperty(String property) {
return properties.get(property);
}
public String getProperty(String property, String default_value) {
String propval = properties.get(property);
if (propval == null)
return default_value;
else
return propval;
}
public StorageAccessIF createAccess(boolean readonly) {
// TODO: Always return same read-only storage access?
String id = "SA" + (++access_counter);
return new RDBMSAccess(id, this, readonly);
}
public TransactionIF createTransaction(boolean readonly) {
AbstractTransaction transaction;
if (readonly)
transaction = new ROTransaction(createAccess(readonly));
else
transaction= new RWTransaction(createAccess(readonly));
synchronized (transactions) {
transactions.add(transaction);
}
return transaction;
}
public boolean isSharedCache() {
return (scache != null);
}
public StorageCacheIF getStorageCache() {
return scache;
}
/**
* INTERNAL: Returns the database type.
*/
public String getDatabase() {
return database;
}
/**
* INTERNAL: Returns the database platforms.
*/
public String[] getPlatforms() {
return platforms;
}
public void close() {
if (cluster != null) {
try {
cluster.leave();
} catch (Throwable t) {
log.error("Could not deregister from cluster.", t);
}
}
if (scache != null) scache.close();
if (saccess != null) saccess.close();
if (rw_connfactory != null) rw_connfactory.close();
if (ro_connfactory != null) ro_connfactory.close();
}
// -----------------------------------------------------------------------------
// Cluster
// -----------------------------------------------------------------------------
public void notifyCluster() {
if (cluster != null) cluster.flush();
}
// -----------------------------------------------------------------------------
// Query cache
// -----------------------------------------------------------------------------
// NOTE: works only with queries with return type object. The return
// values are not transactional object instances, but might instead
// be the identity of such objects.
// NOTE: query caches are indexed by name spaces. There is typically
// one query cache instance per topicmap id + query name.
protected Map<IdentityIF, Map<String, EvictableIF>> qcmap = new HashMap<IdentityIF, Map<String, EvictableIF>>();
public synchronized EvictableIF getHelperObject(int identifier, IdentityIF namespace) {
if (isSharedCache()) {
// get query cache map for namespace
Map<String, EvictableIF> qcm = qcmap.get(namespace);
if (qcm == null) {
qcm = new HashMap<String, EvictableIF>();
qcmap.put(namespace, qcm);
}
switch (identifier) {
case CachesIF.QUERY_CACHE_SRCLOC: {
final String name = "TopicMapIF.getObjectByItemIdentifier";
EvictableIF qc = qcm.get(name);
if (qc == null) {
int lrusize_srcloc = PropertyUtils.getInt(getProperty("net.ontopia.topicmaps.impl.rdbms.Cache.subjectidentity.srcloc.lru"), 2000);
CacheIF cache = caches.createCache(CachesIF.QUERY_CACHE_SRCLOC, namespace);
qc = new QueryCache(createDetachedQuery(name), cache, lrusize_srcloc);
qcm.put(name, qc);
}
return qc;
}
case CachesIF.QUERY_CACHE_SUBIND: {
final String name = "TopicMapIF.getTopicBySubjectIdentifier";
EvictableIF qc = qcm.get(name);
if (qc == null) {
int lrusize_subind = PropertyUtils.getInt(getProperty("net.ontopia.topicmaps.impl.rdbms.Cache.subjectidentity.subind.lru"), 1000);
CacheIF cache = caches.createCache(CachesIF.QUERY_CACHE_SUBIND, namespace);
qc = new QueryCache(createDetachedQuery(name), cache, lrusize_subind);
qcm.put(name, qc);
}
return qc;
}
case CachesIF.QUERY_CACHE_SUBLOC: {
final String name = "TopicMapIF.getTopicBySubject";
EvictableIF qc = qcm.get(name);
if (qc == null) {
int lrusize_subloc = PropertyUtils.getInt(getProperty("net.ontopia.topicmaps.impl.rdbms.Cache.subjectidentity.subloc.lru"), 100);
CacheIF cache = caches.createCache(CachesIF.QUERY_CACHE_SUBLOC, namespace);
qc = new QueryCache(createDetachedQuery(name), cache, lrusize_subloc);
qcm.put(name, qc);
}
return qc;
}
case CachesIF.QUERY_CACHE_RT1: {
final String name = "TopicIF.getRolesByType";
EvictableIF qc = qcm.get(name);
if (qc == null) {
int lrusize = PropertyUtils.getInt(getProperty("net.ontopia.topicmaps.impl.rdbms.Cache.rolesbytype.lru"), 1000);
CacheIF cache = caches.createCache(CachesIF.QUERY_CACHE_RT1, namespace);
qc = new QueryCache(createDetachedQuery(name), cache, lrusize);
qcm.put(name, qc);
}
return qc;
}
case CachesIF.QUERY_CACHE_RT2: {
final String name = "TopicIF.getRolesByType2";
EvictableIF li = qcm.get(name);
if (li == null) {
int lrusize = PropertyUtils.getInt(getProperty("net.ontopia.topicmaps.impl.rdbms.Cache.rolesbytype2.lru"), 1000);
CacheIF cache = caches.createCache(CachesIF.QUERY_CACHE_RT2, namespace);
// ISSUE: why do we do this differently?
li = new TransactionalLRULookupIndex(cache, lrusize);
qcm.put(name, li);
}
return li;
}
default:
throw new OntopiaRuntimeException("No helper object with identifier " + identifier + " found.");
}
} else {
throw new OntopiaRuntimeException("Cannot create helper objects when shared cache is disabled.");
}
}
private DetachedQueryIF createDetachedQuery(String name) {
StorageCacheIF scache = getStorageCache();
// Generate query from query descriptor.
QueryDescriptor qdesc = getQueryDescriptor(name);
return qdesc.createSharedQuery(this, scache.getRegistrar(), platforms);
}
// -----------------------------------------------------------------------------
// Queries
// -----------------------------------------------------------------------------
public String getQueryString(String name) {
QueryDescriptor qdesc = getQueryDescriptor(name);
return qdesc.getStatement(platforms);
}
protected QueryDescriptor getQueryDescriptor(String name) {
// Lookup query descriptor
QueryDescriptor qdesc = queries.getQueryDescriptor(name);
if (qdesc == null)
throw new OntopiaRuntimeException("No query with the name " + name + " found.");
if (log.isDebugEnabled())
log.debug("Generating query '" + name + "' from descriptor.");
return qdesc;
}
public QueryIF createQuery(String name, RDBMSAccess access,
ObjectAccessIF oaccess, AccessRegistrarIF registrar) {
// Generate query from query descriptor.
QueryDescriptor qdesc = getQueryDescriptor(name);
return qdesc.createQuery(access, oaccess, registrar, platforms);
}
public QueryIF createQuery(JDOQuery jdoquery, RDBMSAccess access,
ObjectAccessIF oaccess, AccessRegistrarIF registrar,
boolean lookup_identities) {
//! System.out.println("JDO: " + jdoquery);
SQLQuery sqlquery = sqlbuilder.makeQuery(jdoquery, oaccess);
boolean debug = log.isDebugEnabled();
if (debug)
log.debug("SQL1: " + sqlquery + " [width=" + sqlquery.getWidth() + "]");
//! System.out.println("SQL1: " + sqlquery + " [width=" + sqlquery.getWidth() + "]");
sqlquery = new RedundantTablesSQLOptimizer().optimize(sqlquery);
sqlquery = new EqualsSQLOptimizer().optimize(sqlquery);
SQLStatementIF stm = sqlgen.createSQLStatement(sqlquery);
if (debug)
log.debug("SQL2: " + stm + " [width=" + stm.getWidth() + "]");
//! System.out.println("SQL2: " + stm + " [width=" + stm.getWidth() + "]");
stm.setObjectAccess(oaccess);
stm.setAccessRegistrar(registrar);
return new RDBMSQuery(access, new RDBMSMatrixQuery(stm, lookup_identities));
}
public SQLGeneratorIF getSQLGenerator() {
return sqlgen;
}
public ConnectionFactoryIF getConnectionFactory(boolean readonly) {
return (readonly ? ro_connfactory : rw_connfactory);
}
// -----------------------------------------------------------------------------
// Cache reset
// -----------------------------------------------------------------------------
public synchronized void clearCache() {
if (scache != null) {
// clear shared cache
((SharedCache)scache).clear(true);
// clear helper objects
this.qcmap = new HashMap<IdentityIF, Map<String, EvictableIF>>();
}
}
public void clearCache(IdentityIF namespace) {
if (isSharedCache()) {
EvictableIF ho;
ho = getHelperObject(CachesIF.QUERY_CACHE_SRCLOC, namespace);
ho.clear(true);
ho = getHelperObject(CachesIF.QUERY_CACHE_SUBIND, namespace);
ho.clear(true);
ho = getHelperObject(CachesIF.QUERY_CACHE_SUBLOC, namespace);
ho.clear(true);
ho = getHelperObject(CachesIF.QUERY_CACHE_RT1, namespace);
ho.clear(true);
ho = getHelperObject(CachesIF.QUERY_CACHE_RT2, namespace);
ho.clear(true);
}
}
public void writeReport(java.io.Writer out, TopicMapReferenceIF reference, IdentityIF namespace,
boolean dumpCaches) throws java.io.IOException {
out.write("<h1>OKS statistics</h1>\n");
//! Runtime runtime = Runtime.getRuntime();
//! out.write("<p>");
//! out.write("Processors: ");
//! out.write(Integer.toString(runtime.availableProcessors()));
//! out.write(", Max memory: ");
//! out.write(Long.toString(runtime.maxMemory()));
//! out.write(", Used memory: ");
//! out.write(Long.toString(runtime.totalMemory() - runtime.freeMemory()));
//! out.write(", Total memory: ");
//! out.write(Long.toString(runtime.totalMemory()));
//! out.write(", Free memory: ");
//! out.write(Long.toString(runtime.freeMemory()));
//! out.write("</p>");
// output shared cache statistics
if (scache != null) {
out.write("<h3>Shared cache</h3>\n");
((SharedCache)scache).writeReport(out, dumpCaches);
}
// output query cache statistics
if (isSharedCache()) {
QueryCache qc;
out.write("<h3>TopicMapIF.getObjectByItemIdentifier</h3>\n");
qc = (QueryCache)getHelperObject(CachesIF.QUERY_CACHE_SRCLOC, namespace);
qc.writeReport(out, dumpCaches);
out.write("<h3>TopicMapIF.getTopicBySubjectIdentifier</h3>\n");
qc = (QueryCache)getHelperObject(CachesIF.QUERY_CACHE_SUBIND, namespace);
qc.writeReport(out, dumpCaches);
out.write("<h3>TopicMapIF.getTopicBySubject</h3>\n");
qc = (QueryCache)getHelperObject(CachesIF.QUERY_CACHE_SUBLOC, namespace);
qc.writeReport(out, dumpCaches);
out.write("<h3>TopicIF.getRolesByType</h3>\n");
qc = (QueryCache)getHelperObject(CachesIF.QUERY_CACHE_RT1, namespace);
qc.writeReport(out, dumpCaches);
out.write("<h3>TopicIF.getRolesByType2</h3>\n");
TransactionalLRULookupIndex tl = (TransactionalLRULookupIndex)getHelperObject(CachesIF.QUERY_CACHE_RT2, namespace);
tl.writeReport(out, dumpCaches);
}
// output reference statistics
if (reference != null && reference instanceof RDBMSTopicMapReference) {
out.write("<h3>Transactions</h3>\n");
((RDBMSTopicMapReference)reference).writeReport(out, dumpCaches);
}
// output dbcp statistics
if (rw_connfactory instanceof DBCPConnectionFactory) {
out.write("<h3>DBCP rw connection pool</h3>\n");
((DBCPConnectionFactory)rw_connfactory).writeReport(out);
}
if (ro_connfactory instanceof DBCPConnectionFactory) {
out.write("<h3>DBCP ro connection pool</h3>\n");
((DBCPConnectionFactory)ro_connfactory).writeReport(out);
}
// output storage properties
out.write("<h3>Database properties</h3>\n");
out.write("<p><i>The properties given in bold face are those actually recognized by the OKS.</i></p>\n");
List<String> props = new ArrayList<String>(properties.keySet());
Collections.sort(props);
for (String prop : props) {
if ("net.ontopia.topicmaps.impl.rdbms.Password".equals(prop)) {
out.write("<b>" + prop + "</b>=(<i>hidden for security reasons</i>)<br>\n");
} else {
if (known_properties.contains(prop))
out.write("<b>" + prop + "</b>=" + properties.get(prop) + "<br>\n");
else
out.write(prop + "=" + properties.get(prop) + "<br>\n");
}
}
}
/**
* INTERNAL: Propagates committed merges to other active transactions.
* @param source Identity of the merge source object
* @param target Identity of the merge target object
* @param cause The transaction that committed the merge
* @since %NEXT%
*/
public void objectMerged(IdentityIF source, IdentityIF target, AbstractTransaction cause) {
// block other transactons until we have processed the merge
synchronized (transactions) {
for (AbstractTransaction transaction : transactions) {
if (!transaction.equals(cause)) {
transaction.objectMerged(source, target);
}
}
}
}
public int getActiveTransactionCount() {
return transactions.size();
}
void transactionClosed(AbstractTransaction transaction) {
transactions.remove(transaction);
}
}