All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.datasource.XMATabularDataSourceServer Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

// @(#) $Id: XMATabularDataSourceServer.java 4042 2009-08-27 09:10:15Z gub $
package at.spardat.xma.datasource;

import java.util.HashMap;

import at.spardat.enterprise.cache.DefaultCache;
import at.spardat.enterprise.cache.ICacheDescriptor;
//import at.spardat.enterprise.dom.CacheFilter;
import at.spardat.enterprise.exc.SysException;
import at.spardat.properties.XProperties;
import at.spardat.xma.exception.Codes;
import at.spardat.xma.monitoring.TimeingEvent;
import at.spardat.xma.security.XMAContext;
import at.spardat.xma.session.XMASession;
import at.spardat.xma.session.XMASessionServer;

/**
 * The purpose of this class if twofold. First, it implements {@link ITabularDataSource} at
 * the server side of XMA. Second, it is responsible for querying the origin data source
 * at the server side (database, legacy system or something else). 

* * If a XMA application defines additional data source types, it must subclass this * class and call the method {@link #addTableProvider(String, ITableProvider)} in the * derived class's constructor to add additional handler objects that must implement {@link ITableProvider}. * Basically, ITableProvider is responsible for querying tables for * a particular type and has the methods * provideTable, getExpireDurationClientSecs and * getExpireDurationServerSecs. The first method actually gathers the data * and the other control caching behaviour.

* * getExpireDurationClientSecs defines how long the returned * table may stay in the XMA client cache without querying the XMA server again. * getExpireDurationServerSecs defines how long the table may reside in * the server cache without calling provideTable again. Values of zero * indicate that caches effectively are ignored.

* * Note that the expiration mechanism effectively avoids requests to next higher * cache for the specified amount of time, even if the ressource has changed * at the origin server in the meantime. Therefore, in the worst case, a XMA client might see * a ressource that has been changed getExpireDurationClientSecs + getExpireDurationServerSecs * seconds ago. A XMA server might see a ressource that has been changed * getExpireDurationServerSecs seconds ago in the worst case.

* * If a table is considered to be expired at the client, the client validates the table * by issuing an HTTP conditional get. The recipient of the HTTP get, the XMA server, * itself looks up its cache. If the table is also expired in the server cache, * provideTable is called.

* * This class registers only an ITableProvider for type 'rsc', i.e. resource bundles. * * @author YSD, 16.06.2003 18:29:08 */ public class XMATabularDataSourceServer implements ITabularDataSource, ITableProvider { /** * Constant for a timestamp that is unknown. May be used to specify unknown * last modified timestamps. */ public static final long UNKNOWN_TIMESTAMP = Long.MIN_VALUE; /** * Manages a set of caches, one cache per type of data source. The key * in this map is a String, the type of the data source. Values * are of type TypeCache. */ private HashMap typeCaches_ = new HashMap(); /** * Holds a set of predefined {@link ITableProvider} objects for predefined * data source types. Keys are data source types, values are ITableProvider * objects. */ private HashMap providers_ = new HashMap(); /** * Constructs and installs predefined table providers. */ public XMATabularDataSourceServer () { /** * Install the predefined providers */ addTableProvider ("rsc", new RessourceBundleProviderServer()); } /** * Installs a ITableProvider for a given type. If requests for * tabular data sources are made and the provided type is specified, * the call is delegated to the given provider. */ public void addTableProvider (String type, ITableProvider provider) { providers_.put(type, provider); } /** * This method is not indented to be overwritten. * * @see at.spardat.xma.datasource.ITabularDataSource#getTable(java.lang.String, at.spardat.xma.session.XMASession) */ public ITabularData getTable (String spec, XMASession session) { TableSpec tableSpec = getTableSpec(spec, session); ProviderResultServer result = getTableFromServerCache(session, tableSpec, ITabularDataSource.UNKNOWN_TIMESTAMP); return result.table_; } /** * Creates a TableSpec from the String parameter spec. * If spec is not valid a sysException is thrown. * @param spec * @param session * @return * @since version_number * @author s3460 */ private TableSpec getTableSpec(String spec, XMASession session){ TableSpec tableSpec = new TableSpec (spec); if (!tableSpec.isValid()) throw new SysException ("invalid table spec " + spec).setCode(Codes.DS_INVALID_TABLE_SPEC); tableSpec.addContextParams(session); return tableSpec; } /** * @see at.spardat.xma.datasource.ITabularDataSource#getDomTable(java.lang.String, at.spardat.xma.session.XMASession) */ public ITabularDomData getDomTable(String spec, XMASession session) { ITabularData table = getTable (spec, session); if (!(table instanceof ITabularDomData)) throw new IllegalStateException (spec + " is not a domain table"); return (ITabularDomData)table; } /** * Removes the resource specified by spec from memory. * The resource is removed from a cache hold by TabularDataSourceServer * or by TableManager. * @param spec * @author s3460 */ public void invalidate(String spec){ TableSpec tableSpec = new TableSpec (spec); if (!tableSpec.isValid()){ throw new SysException ("invalid table spec " + spec).setCode(Codes.DS_INVALID_TABLE_SPEC); } String type = tableSpec.getType(); clearCacheHaving(spec,type); } /** * Removes the resource specified by spec from the cache * which holds the resources of type. * @param spec * @param type * @since version_number * @author s3460 */ private void clearCacheHaving(String spec, String type){ TypeCache cache = null; synchronized (typeCaches_) { cache = (TypeCache) typeCaches_.get(type); if (cache != null) { //cache.cache_.print(); //DEBUG //Open Source change: now use at.spardat.xma.datasource.CacheFilter instead of at.spardat.enterprise.dom.CacheFilter cache.cache_.removeAllHaving(new CacheFilter(spec)); //cache.cache_.print(); //DEBUG } } } /** * Requests a table by looking up the server cache (if any). If the server cache * lookup is not successful, {@link #provideTable(XMASession, TableSpec, long)} is called. * * @param session the active XMASession * @param spec a valid table spec (not checked here for validity, caller must * do that). Spec must include context params as done by {@link TableSpec#addContextParams(XMAContext)}. * @param lastModified the last modified timestamp, if the caller knows one * or {@link ITabularDataSource#UNKNOWN_TIMESTAMP} otherwise. * @return null if lastModified is known and the table did not * change. Otherwise not null. */ ProviderResultServer getTableFromServerCache (XMASession session, TableSpec spec, long lastModified) { ProviderResultServer result = null; String type = spec.getType(); /** * first, determine if entries of this type are cached at all. */ int ageServer = getExpireDurationServerSecs(type); if (ageServer > 0) { /** * The cache must be used. First, get the right cache for the provided type */ TypeCache cache = null; synchronized (typeCaches_) { cache = (TypeCache) typeCaches_.get(type); if (cache == null) { cache = new TypeCache (type, ageServer); typeCaches_.put(type, cache); } } /** * variable cache holds the right cache, either retrieved or newly created. * Look up the cache for spec */ String specAsString = spec.toString(); result = (ProviderResultServer) cache.cache_.lookup(specAsString); if (result == null) { /** * The hard part: The entry is not in the cache. We must call * provideTable and insert it into the cache. */ result = provideTableSafe (session, spec, UNKNOWN_TIMESTAMP); cache.cache_.insert(specAsString, result); } String contextPath = ((XMASessionServer)session).getContextPath(); if(contextPath.length() == 0) contextPath = "xma"; TimeingEvent.success("app<"+contextPath+">:tabularCache<"+type+">:size", cache.cache_.getCurrentSize()); // TimeingEvent.success("app<"+contextPath+">:tabularCache<"+type+">:access", (int)cache.cache_.getCntAccess()); // TimeingEvent.success("app<"+contextPath+">:tabularCache<"+type+">:hitratio", (int)((cache.cache_.getCntHit()/(double)cache.cache_.getCntAccess())*100)); } else { // no server cache is used result = provideTableSafe (session, spec, lastModified); } /** * if the lastModified timestamp was provided and did not change, we end up * with the result null. */ if (result != null && lastModified != UNKNOWN_TIMESTAMP && result.lastModified_ == lastModified) { result = null; } return result; } /** * This method is a wrapper around {@link #provideTable()} which does error checking * on the result. Additionally, if the returned last modified timestamp is unknown, * it computes an artificial one. */ private ProviderResultServer provideTableSafe (XMASession session, TableSpec spec, long lastModified) { ProviderResultServer result = provideTable (session, spec, lastModified); if (lastModified == ITabularDataSource.UNKNOWN_TIMESTAMP && result == null) { throw new SysException ("provideTable called for datasource " + spec.toString() + " returned null") .setCode(Codes.DS_PROVIDE_TABLE_RETURNS_NULL); } if (result != null && result.table_ == null) { throw new SysException ("provideTable called for datasource " + spec.toString() + " returned a null table") .setCode(Codes.DS_PROVIDE_TABLE_RETURNS_NULL_TABLE); } /** * compute last modified timestamp */ if (result != null && result.lastModified_ == UNKNOWN_TIMESTAMP) result.lastModified_ = tableHash2randomTimeStamp(result.table_.hashCode()); return result; } /** * The default implemententation is to consult the installed table providers. * * @see at.spardat.xma.datasource.ITableProvider#provideTable(XMASession, at.spardat.xma.datasource.TableSpec, long) * @exception SysException if no provider for the type can be found. */ public ProviderResultServer provideTable (XMASession session, TableSpec spec, long lastModified) { return getTableProviderSafe (spec.getType()).provideTable(session, spec, lastModified); } /** * The default implemententation is to consult the installed table providers. * * @see at.spardat.xma.datasource.ITableProvider#getExpireDurationClientSecs(java.lang.String) * @exception SysException if no provider for the type can be found. */ public int getExpireDurationClientSecs (String type) { return getTableProviderSafe(type).getExpireDurationClientSecs(type); } /** * The default implemententation is to consult the installed table providers. * * @see at.spardat.xma.datasource.ITableProvider#getExpireDurationServerSecs(java.lang.String) * @exception SysException if no provider for the type can be found. */ public int getExpireDurationServerSecs (String type) { return getTableProviderSafe(type).getExpireDurationServerSecs(type); } /** * Returns the ITableProvider for a given tabular datasource type. * * @param type the requested type * @return ITableProvider never null. * @exception SysException if there is no provider for type. */ protected ITableProvider getTableProviderSafe (String type) { ITableProvider provider = (ITableProvider) providers_.get(type); if (provider == null) throw new SysException ("no table provider for type " + type + " installed.") .setCode(Codes.DS_NO_TABLE_PROVIDER); return provider; } // long value of the date 1.7.2003 00:00:00.000 GMT private static final long time_1_7_2003 = 1057017600000L; /** * Maps a given hash code to a timestamp ranging from 1.1.1970 up to 1.7.2003. The * purpose of this method is to generate an artificial lastModified timestamp for * tabular datas which are not able to provide one. * * @param hashCode the input hashCode * @return a long timestamp (milliseconds since 1.1.1970 UTC). */ protected static long tableHash2randomTimeStamp (int hashCode) { long hash = (((long)hashCode)-(long)Integer.MIN_VALUE) * 1000; return hash % time_1_7_2003; } private int getMaxServerCacheSize(String type) { XProperties node = XProperties.getNodeOfPackage("at.spardat.xma.datasource."+type); String size = node.get("ServerMemCacheMaxSize",null); if(size!=null) { return Integer.parseInt(size); } else { node = XProperties.getNodeOfPackage("at.spardat.xma.datasource"); size = node.get("ServerMemCacheMaxSize",null); if(size!=null) { return Integer.parseInt(size); } else { return -1; } } } /** * Encapsulates the cache and its descriptor */ private class TypeCache { TypeCache (String type, int expirationDurationSecs) { String name = "TabularData of type " + type; descriptor_ = new CacheDescriptor (name, expirationDurationSecs, getMaxServerCacheSize(type)); cache_ = new DefaultCache (descriptor_); } DefaultCache cache_; ICacheDescriptor descriptor_; } /** * Descriptor necessary for a cache */ private class CacheDescriptor extends ICacheDescriptor { String name_; int expirationDurationSecs_; int maxSize_; /** * Construcs a CacheDescriptor that is necessaray for a cache * * @param name the name of the cache * @param expirationDurationSecs the max age of entries in the cache */ public CacheDescriptor (String name, int expirationDurationSecs, int maxSize) { name_ = name; expirationDurationSecs_ = expirationDurationSecs; maxSize_=maxSize; } /** * @see at.spardat.enterprise.cache.ICacheDescriptor#getMaxAgeMillis() */ public long getMaxAgeMillis() { return (long)(expirationDurationSecs_)*1000L; } /** * Spread is beeing set to 10 percent. * * @see at.spardat.enterprise.cache.ICacheDescriptor#getMaxAgeSpreadPct() */ public int getMaxAgeSpreadPct() { return 10; } /** * @see at.spardat.enterprise.cache.ICacheDescriptor#getMaxSize() */ public int getMaxSize() { return maxSize_; } /** * @see at.spardat.enterprise.cache.ICacheDescriptor#getName() */ public String getName() { return name_; } /** * @see at.spardat.enterprise.cache.ICacheDescriptor#isTransparent() */ public boolean isTransparent() { return false; } /** * @see at.spardat.enterprise.cache.ICacheDescriptor#load(java.lang.Object) */ public Object load(Object key) { throw new IllegalStateException(); } } // public static void main(String[] args) throws Exception { // SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SSS z"); // SimpleDateFormat df2 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); // java.util.Date d0 = df.parse("1.1.1970 00:00:00.000 GMT"); // java.util.Date d1 = df.parse("1.7.2003 00:00:00.000 GMT"); // // System.out.println ("d0: " + d0.getTime()); // System.out.println ("d1: " + d1.getTime()); // // for (int i=0; i<10000; i++) { // long time = tableHash2randomTimeStamp(TestUtil.randomString(50).hashCode()); // System.out.println (df2.format(new java.util.Date (time)) + " " + time); // } // } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy