Package org.pentaho.reporting.engine.classic.core.cache

Source Code of org.pentaho.reporting.engine.classic.core.cache.CachingDataFactory

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors..  All rights reserved.
*/

package org.pentaho.reporting.engine.classic.core.cache;

import java.util.HashMap;
import java.util.Iterator;
import javax.swing.table.TableModel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.CompoundDataFactory;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.DataRow;
import org.pentaho.reporting.engine.classic.core.MetaTableModel;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.ResourceBundleFactory;
import org.pentaho.reporting.engine.classic.core.StaticDataRow;
import org.pentaho.reporting.engine.classic.core.metadata.DataFactoryMetaData;
import org.pentaho.reporting.engine.classic.core.metadata.DataFactoryRegistry;
import org.pentaho.reporting.engine.classic.core.util.CloseableTableModel;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;

/**
* Creation-Date: 19.11.2006, 13:35:45
*
* @author Thomas Morgner
*/
public class CachingDataFactory implements DataFactory
{
  private static final Log logger = LogFactory.getLog(CachingDataFactory.class);

  private static final Object NULL_INDICATOR = new Object();

  private DataCache dataCache;
  private HashMap<String, HashMap<StaticDataRow, Object>> queryCache;
  private CompoundDataFactory backend;
  private boolean closed;
  private boolean debugDataSources;
  private boolean profileDataSources;
  private boolean noClose;
  private static final String[] EMPTY_NAMES = new String[0];

  public CachingDataFactory(final DataFactory backend, final boolean dataCacheEnabled)
  {
    this(backend, false, dataCacheEnabled);
  }

  public CachingDataFactory(final DataFactory backend, final boolean noClose, final boolean dataCacheEnabled)
  {
    if (backend == null)
    {
      throw new NullPointerException();
    }
    this.noClose = noClose;
    if (noClose)
    {
      this.backend = CompoundDataFactory.normalize(backend, false);
    }
    else
    {
      this.backend = CompoundDataFactory.normalize(backend, true);
    }

    final Configuration configuration = ClassicEngineBoot.getInstance().getGlobalConfig();
    this.queryCache = new HashMap<String, HashMap<StaticDataRow, Object>>();

    if (dataCacheEnabled)
    {
      this.dataCache = DataCacheFactory.getCache();
    }
    else
    {
      this.dataCache = null;
    }

    this.debugDataSources = "true".equals(configuration.getConfigProperty
        ("org.pentaho.reporting.engine.classic.core.DebugDataSources"));
    this.profileDataSources = "true".equals(configuration.getConfigProperty
        ("org.pentaho.reporting.engine.classic.core.ProfileDataSources"));

  }

  public void initialize(final Configuration configuration,
                         final ResourceManager resourceManager,
                         final ResourceKey contextKey,
                         final ResourceBundleFactory resourceBundleFactory)
  {
    backend.initialize(configuration, resourceManager, contextKey, resourceBundleFactory);
  }


  public void open() throws ReportDataFactoryException
  {
    if (noClose == false)
    {
      backend.open();
    }
  }

  public boolean isQueryExecutable(final String query, final DataRow parameters)
  {
    if (query == null)
    {
      throw new NullPointerException();
    }
    if (parameters == null)
    {
      throw new NullPointerException();
    }

    if (backend.isQueryExecutable(query, parameters))
    {
      return true;
    }
    return false;
  }

  /**
   * Queries a datasource. The string 'query' defines the name of the query. The Parameterset given here may contain
   * more data than actually needed.
   * <p/>
   * The dataset may change between two calls, do not assume anything!
   *
   * @param query
   * @param parameters
   * @return
   */
  public TableModel queryData(final String query, final DataRow parameters)
      throws ReportDataFactoryException
  {
    if (query == null)
    {
      throw new NullPointerException();
    }
    if (parameters == null)
    {
      throw new NullPointerException();
    }

    final DataFactory dataFactoryForQuery = backend.getDataFactoryForQuery(query);
    final DataCacheKey key;
    if (dataFactoryForQuery != null && dataCache != null)
    {
      // search the datafactory that executes a query
      // metadata: query fields that are used
      // metadata: get a query-string hash-object (or the raw query)
      final String metaKey = dataFactoryForQuery.getClass().getName();
      final DataFactoryMetaData metaData = DataFactoryRegistry.getInstance().getMetaData(metaKey);
      final String[] referencedFields = metaData.getReferencedFields(dataFactoryForQuery, query, parameters);
      if (referencedFields != null)
      {
        final Object queryHash = metaData.getQueryHash(dataFactoryForQuery, query, parameters);
        if (queryHash == null)
        {
          key = null;
        }
        else
        {
          key = new DataCacheKey();
          for (int i = 0; i < referencedFields.length; i++)
          {
            final String field = referencedFields[i];
            key.addParameter(field, parameters.get(field));
          }

          key.addAttribute(DataCacheKey.QUERY_CACHE, queryHash);

          final TableModel model = dataCache.get(key);
          if (model != null)
          {
            if (model instanceof MetaTableModel)
            {
              return new IndexedMetaTableModel((MetaTableModel) model);
            }
            else
            {
              return new IndexedTableModel(model);
            }
          }
        }
      }
      else
      {
        key = null;
      }
    }
    else
    {
      key = null;
    }


    if (backend.isQueryExecutable(query, parameters))
    {
      TableModel data = queryInternal(query, parameters);
      if (data != null)
      {
        if (key != null)
        {
          final TableModel newData = dataCache.put(key, data);
          if (newData != data && data instanceof CloseableTableModel)
          {
            final CloseableTableModel closeableTableModel = (CloseableTableModel) data;
            closeableTableModel.close();
          }
          data = newData;
        }
       
        if (data instanceof MetaTableModel)
        {
          return new IndexedMetaTableModel((MetaTableModel) data);
        }
        else
        {
          return new IndexedTableModel(data);
        }
      }
    }
    throw new ReportDataFactoryException("The specified query '" + query + "' is not executable here.");
  }

  private TableModel queryInternal(final String query, final DataRow parameters)
      throws ReportDataFactoryException
  {
    if (profileDataSources && CachingDataFactory.logger.isDebugEnabled())
    {
      CachingDataFactory.logger.debug(System.identityHashCode(
          Thread.currentThread()) + ": Query processing time: Starting");
    }
    final long startTime = System.currentTimeMillis();
    try
    {
      final HashMap<StaticDataRow, Object> parameterCache = queryCache.get(query);
      if (parameterCache == null)
      {

        final StaticDataRow params = new StaticDataRow(parameters);
        final TableModel dataFromQuery = backend.queryData(query, params);
        if (dataFromQuery == null)
        {
          //final DefaultTableModel value = new DefaultTableModel();
          if (debugDataSources && CachingDataFactory.logger.isDebugEnabled())
          {
            CachingDataFactory.logger.debug("Query failed for query '" + query + '\'');
          }
          final HashMap<StaticDataRow, Object> paramsForQueryMap = new HashMap<StaticDataRow, Object>();
          queryCache.put(query, paramsForQueryMap);
          paramsForQueryMap.put(params, NULL_INDICATOR);
          return null;
        }
        else
        {
          if (debugDataSources && CachingDataFactory.logger.isDebugEnabled())
          {
            CachingDataFactory.printTableModelContents(dataFromQuery);
          }
          // totally new query here.
          final HashMap<StaticDataRow, Object> paramsForQueryMap = new HashMap<StaticDataRow, Object>();
          queryCache.put(query, paramsForQueryMap);
          paramsForQueryMap.put(params, dataFromQuery);
          return dataFromQuery;
        }
      }
      else
      {
        // Lookup the parameters ...
        final StaticDataRow params = new StaticDataRow(parameters);
        final Object dataObj = parameterCache.get(params);
        if (dataObj == NULL_INDICATOR)
        {
          // query is known to be null for the given parameters ...
          return null;
        }

        final TableModel data = (TableModel) dataObj;
        if (data != null)
        {
          return data;
        }

        final TableModel newData = backend.queryData(query, params);
        if (newData == null)
        {
          if (debugDataSources && CachingDataFactory.logger.isDebugEnabled())
          {
            CachingDataFactory.logger.debug("Query failed for query '" + query + '\'');
          }
          parameterCache.put(params, NULL_INDICATOR);
          return null;
        }
        else
        {
          if (debugDataSources && CachingDataFactory.logger.isDebugEnabled())
          {
            CachingDataFactory.printTableModelContents(newData);
          }
          parameterCache.put(params, newData);
          return newData;
        }
      }
    }
    finally
    {
      final long queryTime = System.currentTimeMillis();
      if (profileDataSources && CachingDataFactory.logger.isDebugEnabled())
      {
        CachingDataFactory.logger.debug(System.identityHashCode(
            Thread.currentThread()) + ": Query processing time: " + ((queryTime - startTime) / 1000.0));
      }
    }
  }

  /**
   * Closes the report data factory and all report data instances that have been returned by this instance.
   */
  public void close()
  {

    if (closed == false)
    {
      final Iterator queries = queryCache.values().iterator();
      while (queries.hasNext())
      {
        final HashMap map = (HashMap) queries.next();
        final Iterator dataSets = map.values().iterator();
        while (dataSets.hasNext())
        {
          final TableModel data = (TableModel) dataSets.next();
          if (data instanceof CloseableTableModel)
          {
            final CloseableTableModel ct = (CloseableTableModel) data;
            ct.close();
          }
        }
      }
      queryCache.clear();
      if (noClose == false)
      {
        backend.close();
      }
      closed = true;
    }
  }

  /**
   * Derives a freshly initialized report data factory, which is independend of the original data factory. Opening or
   * Closing one data factory must not affect the other factories.
   *
   * @return nothing, the method dies instead.
   * @throws UnsupportedOperationException as this class is not derivable.
   */
  public DataFactory derive()
  {
    // If you see that exception, then you've probably tried to use that
    // datafactory from outside of the report processing. You deserve the
    // exception in that case ..
    throw new UnsupportedOperationException
        ("The CachingReportDataFactory cannot be derived.");
  }


  /**
   * Prints a table model to standard output.
   *
   * @param mod the model.
   */
  public static void printTableModelContents(final TableModel mod)
  {
    if (mod == null)
    {
      throw new NullPointerException();
    }

    logger.debug("Tablemodel contains " + mod.getRowCount() + " rows."); //$NON-NLS-1$ //$NON-NLS-2$
    for (int i = 0; i < mod.getColumnCount(); i++)
    {
      logger.debug("Column: " + i + " Name = " + mod.getColumnName(
          i) + "; DataType = " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
          + mod.getColumnClass(i));
    }

    logger.debug("Checking the data inside"); //$NON-NLS-1$
    for (int rows = 0; rows < mod.getRowCount(); rows++)
    {
      for (int i = 0; i < mod.getColumnCount(); i++)
      {
        final Object value = mod.getValueAt(rows, i);
        logger.debug("ValueAt (" + rows + ", " + i + ") is " + value); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
      }
    }
  }


  public String[] getQueryNames()
  {
    return EMPTY_NAMES;
  }

  public void cancelRunningQuery()
  {
    // TODO implement
  }

  public Object clone()
  {
    try
    {
      final CachingDataFactory cdf = (CachingDataFactory) super.clone();
      cdf.backend = (CompoundDataFactory) backend.clone();
      cdf.queryCache = (HashMap<String, HashMap<StaticDataRow, Object>>) queryCache.clone();
      return cdf;
    }
    catch (CloneNotSupportedException e)
    {
      throw new IllegalStateException(e);
    }
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.core.cache.CachingDataFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.