Package org.pentaho.reporting.engine.classic.extensions.datasources.mondrian

Source Code of org.pentaho.reporting.engine.classic.extensions.datasources.mondrian.AbstractMDXDataFactory

/*
* 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) 2008 - 2009 Pentaho Corporation, .  All rights reserved.
*/
package org.pentaho.reporting.engine.classic.extensions.datasources.mondrian;

import java.lang.reflect.Array;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.regex.PatternSyntaxException;

import mondrian.mdx.MemberExpr;
import mondrian.olap.Connection;
import mondrian.olap.Cube;
import mondrian.olap.Exp;
import mondrian.olap.Hierarchy;
import mondrian.olap.Literal;
import mondrian.olap.Member;
import mondrian.olap.MondrianException;
import mondrian.olap.MondrianProperties;
import mondrian.olap.OlapElement;
import mondrian.olap.Parameter;
import mondrian.olap.Position;
import mondrian.olap.Query;
import mondrian.olap.Result;
import mondrian.olap.Util;
import mondrian.olap.type.MemberType;
import mondrian.olap.type.NumericType;
import mondrian.olap.type.SetType;
import mondrian.olap.type.StringType;
import mondrian.olap.type.Type;
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.DataFactory;
import org.pentaho.reporting.engine.classic.core.DataRow;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.ResourceBundleFactory;
import org.pentaho.reporting.engine.classic.core.util.PropertyLookupParser;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.CSVTokenizer;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.formatting.FastMessageFormat;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;

/**
* This data-factory operates in Legacy-Mode providing a preprocessed view on the mondrian result.
* It behaves exactly as known from the Pentaho-Platform and the Pentaho-Report-Designer. This mode
* of operation breaks the structure of the resulting table as soon as new rows are returned by the
* server.
*
* @author Thomas Morgner
*/
public abstract class AbstractMDXDataFactory implements DataFactory, Cloneable
{

  /**
   * The message compiler maps all named references into numeric references.
   */
  protected static class MDXCompiler extends PropertyLookupParser
  {
    private HashSet<String> collectedParameter;
    private DataRow parameters;
    private Locale locale;

    /**
     * Default Constructor.
     */
    protected MDXCompiler(final DataRow parameters,
                          final Locale locale)
    {
      this.collectedParameter = new HashSet<String>();
      this.parameters = parameters;
      this.locale = locale;
      setMarkerChar('$');
      setOpeningBraceChar('{');
      setClosingBraceChar('}');
    }

    /**
     * Looks up the property with the given name. This replaces the name with the current index position.
     *
     * @param name the name of the property to look up.
     * @return the translated value.
     */
    protected String lookupVariable(final String name)
    {
      final CSVTokenizer tokenizer = new CSVTokenizer(name, false);
      if (tokenizer.hasMoreTokens() == false)
      {
        // invalid reference ..
        return null;
      }

      final String parameterName = tokenizer.nextToken();
      collectedParameter.add(parameterName);
      final Object o = parameters.get(parameterName);
      String subType = null;
      final StringBuffer b = new StringBuffer(name.length() + 4);
      b.append('{');
      b.append("0");
      while (tokenizer.hasMoreTokens())
      {
        b.append(',');
        final String token = tokenizer.nextToken();
        b.append(token);
        if (subType == null)
        {
          subType = token;
        }
      }
      b.append('}');
      final String formatString = b.toString();

      if ("string".equals(subType))
      {
        if (o == null)
        {
          return "null"; // NON-NLS
        }
        return quote(String.valueOf(o));
      }

      final FastMessageFormat messageFormat = new FastMessageFormat(formatString, locale);
      return messageFormat.format(new Object[]{o});
    }

    public Set<String> getCollectedParameter()
    {
      return Collections.unmodifiableSet((Set<String>) collectedParameter.clone());
    }
  }

  private static final String ACCEPT_ROLES_CONFIG_KEY = "org.pentaho.reporting.engine.classic.extensions.datasources.mondrian.role-filter.static.accept";
  private static final String ACCEPT_REGEXP_CONFIG_KEY = "org.pentaho.reporting.engine.classic.extensions.datasources.mondrian.role-filter.reg-exp.accept";
  private static final String DENY_ROLE_CONFIG_KEY = "org.pentaho.reporting.engine.classic.extensions.datasources.mondrian.role-filter.static.deny";
  private static final String DENY_REGEXP_CONFIG_KEY = "org.pentaho.reporting.engine.classic.extensions.datasources.mondrian.role-filter.reg-exp.deny";
  private static final String ROLE_FILTER_ENABLE_CONFIG_KEY = "org.pentaho.reporting.engine.classic.extensions.datasources.mondrian.role-filter.enable";

  private String jdbcUser;
  private String jdbcUserField;
  private String jdbcPassword;
  private String jdbcPasswordField;
  private String dynamicSchemaProcessor;
  private Boolean useSchemaPool;
  private Boolean useContentChecksum;

  private String role;
  private String roleField;
  private CubeFileProvider cubeFileProvider;
  private DataSourceProvider dataSourceProvider;
  private MondrianConnectionProvider mondrianConnectionProvider;
  private String designTimeName;
  private transient Locale locale;
  private transient Connection connection;
  private transient ResourceManager resourceManager;
  private transient ResourceKey contextKey;
  private static final String[] EMPTY_QUERYNAMES = new String[0];
  private static final Log logger = LogFactory.getLog(AbstractMDXDataFactory.class);

  public AbstractMDXDataFactory()
  {
    this.mondrianConnectionProvider = new DefaultMondrianConnectionProvider();
  }

  /**
   * Initializes the data factory and provides new context information. Initialize is always called before the
   * datafactory has been opened by calling DataFactory#open.
   *
   * @param configuration         the current report configuration.
   * @param resourceManager       the report's resource manager.
   * @param contextKey            the report's context key to access resources relative to the report location.
   * @param resourceBundleFactory the report's resource-bundle factory to access localization information.
   */
  public void initialize(final Configuration configuration,
                         final ResourceManager resourceManager,
                         final ResourceKey contextKey,
                         final ResourceBundleFactory resourceBundleFactory)
  {
    this.resourceManager = resourceManager;
    this.contextKey = contextKey;
    if (resourceBundleFactory == null)
    {
      locale = Locale.getDefault();
    }
    else
    {
      locale = resourceBundleFactory.getLocale();
    }
  }

  public MondrianConnectionProvider getMondrianConnectionProvider()
  {
    return mondrianConnectionProvider;
  }

  public void setMondrianConnectionProvider(final MondrianConnectionProvider mondrianConnectionProvider)
  {
    if (mondrianConnectionProvider == null)
    {
      throw new NullPointerException();
    }
    this.mondrianConnectionProvider = mondrianConnectionProvider;
  }

  public String getDynamicSchemaProcessor()
  {
    return dynamicSchemaProcessor;
  }

  public void setDynamicSchemaProcessor(final String dynamicSchemaProcessor)
  {
    this.dynamicSchemaProcessor = dynamicSchemaProcessor;
  }

  public Boolean isUseSchemaPool()
  {
    return useSchemaPool;
  }

  public void setUseSchemaPool(final Boolean useSchemaPool)
  {
    this.useSchemaPool = useSchemaPool;
  }

  public Boolean isUseContentChecksum()
  {
    return useContentChecksum;
  }

  public void setUseContentChecksum(final Boolean useContentChecksum)
  {
    this.useContentChecksum = useContentChecksum;
  }

  public String getRole()
  {
    return role;
  }

  public void setRole(final String role)
  {
    this.role = role;
  }

  public String getRoleField()
  {
    return roleField;
  }

  public void setRoleField(final String roleField)
  {
    this.roleField = roleField;
  }

  public CubeFileProvider getCubeFileProvider()
  {
    return cubeFileProvider;
  }

  public void setCubeFileProvider(final CubeFileProvider cubeFileProvider)
  {
    this.cubeFileProvider = cubeFileProvider;
  }

  public DataSourceProvider getDataSourceProvider()
  {
    return dataSourceProvider;
  }

  public void setDataSourceProvider(final DataSourceProvider dataSourceProvider)
  {
    this.dataSourceProvider = dataSourceProvider;
  }

  public String getJdbcUser()
  {
    return jdbcUser;
  }

  public void setJdbcUser(final String jdbcUser)
  {
    this.jdbcUser = jdbcUser;
  }

  public String getJdbcPassword()
  {
    return jdbcPassword;
  }

  public void setJdbcPassword(final String jdbcPassword)
  {
    this.jdbcPassword = jdbcPassword;
  }

  public String getJdbcUserField()
  {
    return jdbcUserField;
  }

  public void setJdbcUserField(final String jdbcUserField)
  {
    this.jdbcUserField = jdbcUserField;
  }

  public String getJdbcPasswordField()
  {
    return jdbcPasswordField;
  }

  public void setJdbcPasswordField(final String jdbcPasswordField)
  {
    this.jdbcPasswordField = jdbcPasswordField;
  }

  /**
   * Checks whether the query would be executable by this datafactory. This performs a rough check, not a full query.
   *
   * @param query
   * @param parameters
   * @return
   */
  public boolean isQueryExecutable(final String query, final DataRow parameters)
  {
    return true;
  }

  public void open() throws ReportDataFactoryException
  {

  }

  /**
   * Closes the data factory and frees all resources held by this instance.
   */
  public void close()
  {
    if (connection != null)
    {
      connection.close();
    }
    connection = null;
  }

  /**
   * Queries a datasource. The string 'query' defines the name of the query. The Parameterset given here may contain
   * more data than actually needed for the query.
   * <p/>
   * The parameter-dataset may change between two calls, do not assume anything, and do not hold references to the
   * parameter-dataset or the position of the columns in the dataset.
   *
   * @param rawMdxQuery the mdx Query string.
   * @param parameters  the parameters for the query
   * @return the result of the query as table model.
   * @throws org.pentaho.reporting.engine.classic.core.ReportDataFactoryException
   *          if an error occured while performing the query.
   */
  public Result performQuery(final String rawMdxQuery, final DataRow parameters) throws ReportDataFactoryException
  {
    try
    {
      if (connection == null)
      {
        connection = mondrianConnectionProvider.createConnection(computeProperties(parameters), dataSourceProvider.getDataSource());
      }
    }
    catch (SQLException e)
    {
      throw new ReportDataFactoryException("Failed to create datasource:" + e.getLocalizedMessage(), e);
    }
    catch (MondrianException e)
    {
      throw new ReportDataFactoryException("Failed to create datasource:" + e.getLocalizedMessage(), e);
    }

    try
    {
      if (connection == null)
      {
        throw new ReportDataFactoryException("Factory is closed.");
      }

      final Object queryTimeout = parameters.get(DataFactory.QUERY_TIMEOUT);
      final int queryTimeoutValue;
      if (queryTimeout instanceof Number)
      {
        final Number i = (Number) queryTimeout;
        queryTimeoutValue = i.intValue();
      }
      else
      {
        // means unlimited ...
        queryTimeoutValue = 0;
      }

      final MDXCompiler compiler = new MDXCompiler(parameters, locale);
      final String mdxQuery = compiler.translateAndLookup(rawMdxQuery, parameters);
      // Alternatively, JNDI is possible. Maybe even more ..
      final Query query = connection.parseQuery(mdxQuery);
      if (queryTimeoutValue > 0)
      {
        query.setQueryTimeoutMillis(queryTimeoutValue * 1000);
      }

      final Parameter[] parameterDefs = query.getParameters();
      for (int i = 0; i < parameterDefs.length; i++)
      {
        final Parameter def = parameterDefs[i];
        Object parameterValue = parameters.get(def.getName());
        final Object processedParamValue;
        final Type parameterType = def.getType();

        // Mondrian doesn't handle null MemberType/SetType parameters well (http://jira.pentaho.com/browse/MONDRIAN-745)
        // If parameterValue is null, give it the default value
        try
        {
          if (parameterValue == null && (parameterType instanceof MemberType || parameterType instanceof SetType))
          {
            parameterValue = def.getDefaultExp().toString();
          }
        }
        catch (Exception e)
        {
          // Ignore - this is a safety procedure anyway
        }

        if (parameterValue != null)
        {

          if (parameterType instanceof StringType)
          {
            if (!(parameterValue instanceof String))
            {
              throw new ReportDataFactoryException(parameterValue + " is incorrect for type " + parameterType);
            }
            processedParamValue = parameterValue;
          }
          else if (parameterType instanceof NumericType)
          {
            if (!(parameterValue instanceof Number))
            {
              throw new ReportDataFactoryException(parameterValue + " is incorrect for type " + parameterType);
            }
            processedParamValue = parameterValue;
          }
          else if (parameterType instanceof MemberType)
          {
            final MemberType memberType = (MemberType) parameterType;
            final Hierarchy hierarchy = memberType.getHierarchy();
            if (parameterValue instanceof String)
            {
              final Member member = findMember(query, hierarchy, query.getCube(), String.valueOf(parameterValue));
              if (member != null)
              {
                processedParamValue = new MemberExpr(member);
              }
              else
              {
                processedParamValue = null;
              }
            }
            else
            {
              if (!(parameterValue instanceof OlapElement))
              {
                throw new ReportDataFactoryException(parameterValue + " is incorrect for type " + parameterType);
              }
              else
              {
                processedParamValue = parameterValue;
              }
            }
          }
          else if (parameterType instanceof SetType)
          {
            final SetType setType = (SetType) parameterType;
            final Hierarchy hierarchy = setType.getHierarchy();
            if (parameterValue instanceof String)
            {
              final String rawString = (String) parameterValue;
              final String[] memberStr = rawString.replaceFirst("^ *\\{", "").replaceFirst("} *$", "").split(",");
              final List<Member> list = new ArrayList<Member>(memberStr.length);

              for (int j = 0; j < memberStr.length; j++)
              {
                final String str = memberStr[j];
                final Member member = findMember(query, hierarchy, query.getCube(), String.valueOf(str));
                if (member != null)
                {
                  list.add(member);
                }
              }

              processedParamValue = list;
            }
            else
            {
              if (!(parameterValue instanceof OlapElement))
              {
                throw new ReportDataFactoryException(parameterValue + " is incorrect for type " + parameterType);
              }
              else
              {
                processedParamValue = parameterValue;
              }
            }
          }
          else
          {
            processedParamValue = parameterValue;
          }
        }
        else
        {
          processedParamValue = null;
        }

        // Mondrian allows null values to be passed in, so we'll go ahead and
        // convert null values to their defaults for now until MONDRIAN-745 is
        // resolved.

        final Exp exp = def.getDefaultExp();
        if (processedParamValue == null && exp != null && exp instanceof Literal)
        {
          def.setValue(((Literal) exp).getValue());
        }
        else
        {
          def.setValue(processedParamValue);
        }
      }

      final Result resultSet = connection.execute(query);
      if (resultSet == null)
      {
        throw new ReportDataFactoryException("query returned no resultset");
      }
      return resultSet;
    }
    catch (MondrianException e)
    {
      throw new ReportDataFactoryException("Failed to create datasource:" + e.getLocalizedMessage(), e);
    }
  }

  private Member findMember(final Query query,
                            final Hierarchy hierarchy,
                            final Cube cube,
                            final String parameter) throws ReportDataFactoryException
  {
    try
    {
      final Member directValue = yuckyInternalMondrianLookup(query, hierarchy, parameter);
      if (directValue != null)
      {
        return directValue;
      }
    }
    catch (Exception e)
    {
      // It is non fatal if that fails. Invalid input has this effect.
    }

    Member memberById = null;
    Member memberByUniqueId = null;

    final boolean searchForNames = MondrianProperties.instance().NeedDimensionPrefix.get() == false;
    final boolean missingMembersIsFatal = MondrianProperties.instance().IgnoreInvalidMembersDuringQuery.get();

    try
    {
      final Member directValue = lookupDirectly(hierarchy, cube, parameter, searchForNames);
      if (directValue != null)
      {
        return directValue;
      }
    }
    catch (Exception e)
    {
      // It is non fatal if that fails. Invalid input has this effect.
    }

    final Query memberQuery = connection.parseQuery("SELECT " + hierarchy.getQualifiedName() // NON-NLS
        + ".AllMembers ON 0, {} ON 1 FROM " + cube.getQualifiedName()); // NON-NLS
    final Result result = connection.execute(memberQuery);
    try
    {
      final List<Position> positionList = result.getAxes()[0].getPositions();
      for (int i = 0; i < positionList.size(); i++)
      {
        final Position position = positionList.get(i);
        for (int j = 0; j < position.size(); j++)
        {
          final Member member = position.get(j);
          if (parameter.equals(MondrianUtil.getUniqueMemberName(member)))
          {
            if (memberByUniqueId == null)
            {
              memberByUniqueId = member;
            }
            else
            {
              logger.warn("Encountered a member with a duplicate unique key: " + member.getQualifiedName()); // NON-NLS
            }
          }
          if (searchForNames == false)
          {
            continue;
          }
          if (parameter.equals(member.getName()))
          {
            if (memberById == null)
            {
              memberById = member;
            }
            else
            {
              logger.warn("Encountered a member with a duplicate name: " + member.getQualifiedName()); // NON-NLS
            }
          }
        }
      }
    }
    finally
    {
      result.close();
    }
    if (memberByUniqueId != null)
    {
      return memberByUniqueId;
    }
    if (memberById != null)
    {
      return memberById;
    }

    if (missingMembersIsFatal)
    {
      throw new ReportDataFactoryException("No member matches parameter value '" + parameter + "'.");
    }
    return null;
  }

  private Member lookupDirectly(final Hierarchy hierarchy,
                                final Cube cube,
                                final String parameter,
                                final boolean searchForNames)
  {
    Member memberById = null;
    Member memberByUniqueId = null;
    final Query queryDirect = connection.parseQuery("SELECT STRTOMEMBER(" + quote(parameter) + ") ON 0, {} ON 1 FROM " // NON-NLS
        + cube.getQualifiedName());
    final Result resultDirect = connection.execute(queryDirect);
    try
    {
      final List<Position> positionList = resultDirect.getAxes()[0].getPositions();
      for (int i = 0; i < positionList.size(); i++)
      {
        final Position position = positionList.get(i);
        for (int j = 0; j < position.size(); j++)
        {
          final Member member = position.get(j);

          // If the parameter starts with '[', we'll assume we have the full
          // member specification specification. Otherwise, keep the funky lookup
          // route. We do check whether we get a second member (heck, should not
          // happen, but I've seen pigs fly already).

          if (parameter.startsWith("["))
          {
            if (memberByUniqueId == null)
            {
              memberByUniqueId = member;
            }
            else
            {
              logger.warn("Encountered a member with a duplicate key: " + member.getQualifiedName()); // NON-NLS
            }
          }
          if (searchForNames == false)
          {
            continue;
          }
          if (parameter.equals(member.getName()))
          {
            if (memberById == null)
            {
              memberById = member;
            }
            else
            {
              logger.warn("Encountered a member with a duplicate name: " + member.getQualifiedName()); // NON-NLS
            }
          }
        }
      }
    }
    finally
    {
      resultDirect.close();
    }
    if (memberByUniqueId != null)
    {
      final Hierarchy memberHierarchy = memberByUniqueId.getHierarchy();
      if (hierarchy != memberHierarchy)
      {
        if (ObjectUtilities.equal(hierarchy, memberHierarchy) == false)
        {
          logger.warn("Cannot match hierarchy of member found with the hierarchy specfied in the parameter: " // NON-NLS
              + "Unabe to guarantee that the correct member has been queried, returning null."); // NON-NLS
          return null;
        }
      }
      return memberByUniqueId;
    }
    if (memberById != null)
    {
      final Hierarchy memberHierarchy = memberById.getHierarchy();
      if (hierarchy != memberHierarchy)
      {
        if (ObjectUtilities.equal(hierarchy, memberHierarchy) == false)
        {
          logger.warn("Cannot match hierarchy of member found with the hierarchy specfied in the parameter: " // NON-NLS
              + "Unabe to guarantee that the correct member has been queried, returning null"); // NON-NLS
          return null;
        }
      }
      return memberById;
    }
    return null;
  }

  protected Member yuckyInternalMondrianLookup(final Query query, final Hierarchy hierarchy, final String parameter)
  {
    final Member memberById = (Member) Util.lookup(query, Util.parseIdentifier(parameter));
    if (memberById != null)
    {
      final Hierarchy memberHierarchy = memberById.getHierarchy();
      if (hierarchy != memberHierarchy)
      {
        if (ObjectUtilities.equal(hierarchy, memberHierarchy) == false)
        {
          logger.warn("Cannot match hierarchy of member found with the hierarchy specfied in the parameter: " // NON-NLS
              + "Unabe to guarantee that the correct member has been queried, returning null"); // NON-NLS
          return null;
        }
      }
      return memberById;
    }
    return null;
  }

  protected int extractQueryLimit(final DataRow parameters)
  {
    final Object queryLimit = parameters.get(DataFactory.QUERY_LIMIT);
    final int queryLimitValue;
    if (queryLimit instanceof Number)
    {
      final Number i = (Number) queryLimit;
      queryLimitValue = Math.max(0, i.intValue());
    }
    else
    {
      // means no limit at all
      queryLimitValue = 0;
    }
    return queryLimitValue;
  }

  private String computeRole(final DataRow parameters) throws ReportDataFactoryException
  {
    if (roleField != null)
    {
      final Object field = parameters.get(roleField);
      if (field != null)
      {
        if (field instanceof Object[])
        {
          final Object[] roleArray = (Object[]) field;
          final StringBuffer buffer = new StringBuffer();
          final int length = roleArray.length;
          for (int i = 0; i < length; i++)
          {
            final Object o = roleArray[i];
            if (o == null)
            {
              continue;
            }

            final String role = filter(String.valueOf(o));
            if (role == null)
            {
              continue;
            }
            buffer.append(quoteRole(role));
          }
          return buffer.toString();
        }
        else if (field.getClass().isArray())
        {
          final StringBuffer buffer = new StringBuffer();
          final int length = Array.getLength(field);
          for (int i = 0; i < length; i++)
          {
            final Object o = Array.get(field, i);
            if (o == null)
            {
              continue;
            }

            final String role = filter(String.valueOf(o));
            if (role == null)
            {
              continue;
            }
            buffer.append(quoteRole(role));
          }
          return buffer.toString();
        }
        final String role = filter(String.valueOf(field));
        if (role != null)
        {
          return role;
        }
      }
    }

    return filter(role);
  }

  private String quoteRole(final String role)
  {
    if (role.indexOf(',') == -1)
    {
      return role;
    }
    final StringBuffer b = new StringBuffer(role.length() + 5);
    final char[] chars = role.toCharArray();
    for (int i = 0; i < chars.length; i++)
    {
      final char c = chars[i];
      if (c == ',')
      {
        b.append(c);
      }
      b.append(c);
    }
    return b.toString();
  }

  private String computeJdbcUser(final DataRow parameters)
  {
    if (jdbcUserField != null)
    {
      final Object field = parameters.get(jdbcUserField);
      if (field != null)
      {
        return String.valueOf(field);
      }
    }

    return jdbcUser;
  }

  private String computeJdbcPassword(final DataRow parameters)
  {
    if (jdbcPasswordField != null)
    {
      final Object field = parameters.get(jdbcPasswordField);
      if (field != null)
      {
        return String.valueOf(field);
      }
    }

    return jdbcPassword;
  }

  private Properties computeProperties(final DataRow parameters) throws ReportDataFactoryException
  {
    if (cubeFileProvider == null)
    {
      throw new ReportDataFactoryException("No CubeFileProvider");
    }

    final Properties properties = new Properties();
    final String catalog = cubeFileProvider.getCubeFile(resourceManager, contextKey);
    if (catalog == null)
    {
      throw new ReportDataFactoryException("No valid catalog given.");
    }
    properties.setProperty("Catalog", catalog); // NON-NLS
    final String role = computeRole(parameters);
    if (role != null)
    {
      properties.setProperty("Role", role); // NON-NLS
    }
    final String jdbcUser = computeJdbcUser(parameters);
    if (jdbcUser != null)
    {
      properties.setProperty("JdbcUser", jdbcUser); // NON-NLS
    }
    final String jdbcPassword = computeJdbcPassword(parameters);
    if (jdbcPassword != null)
    {
      properties.setProperty("JdbcPassword", jdbcPassword); // NON-NLS
    }
    if (locale != null)
    {
      properties.setProperty("Locale", locale.toString()); // NON-NLS
    }

    if (isUseContentChecksum() != null)
    {
      properties.setProperty("UseContentChecksum", String.valueOf(isUseContentChecksum())); // NON-NLS
    }
    if (isUseSchemaPool() != null)
    {
      properties.setProperty("UseSchemaPool", String.valueOf(isUseSchemaPool())); // NON-NLS
    }
    if (getDynamicSchemaProcessor() != null)
    {
      properties.setProperty("DynamicSchemaProcessor", getDynamicSchemaProcessor()); // NON-NLS
    }
    return properties;
  }

  /**
   * Returns a copy of the data factory that is not affected by its anchestor and holds no connection to the anchestor
   * anymore. A data-factory will be derived at the beginning of the report processing.
   *
   * @return a copy of the data factory.
   */
  public DataFactory derive()
  {
    return (DataFactory) clone();
  }

  public Object clone()
  {
    try
    {
      final AbstractMDXDataFactory dataFactory = (AbstractMDXDataFactory) super.clone();
      dataFactory.connection = null;
      return dataFactory;
    }
    catch (CloneNotSupportedException e)
    {
      throw new IllegalStateException("Failed", e);
    }
  }

  public String getDesignTimeName()
  {
    return designTimeName;
  }

  public void setDesignTimeName(final String designTimeName)
  {
    this.designTimeName = designTimeName;
  }

  /**
   * Returns all known query-names. A data-factory may accept more than the query-names returned here.
   *
   * @return the known query names.
   */
  public String[] getQueryNames()
  {
    return EMPTY_QUERYNAMES;
  }

  /**
   * Attempts to cancel the query process that is generating the data for this data factory.
   * If it is not possible to cancel the query, this call should be ignored.
   */
  public void cancelRunningQuery()
  {
  }

  protected static String quote(final String original)
  {
    // This solution needs improvements. Copy blocks instead of single
    // characters.
    final int length = original.length();
    final StringBuffer b = new StringBuffer(length * 12 / 10);
    b.append('"');

    for (int i = 0; i < length; i++)
    {
      final char c = original.charAt(i);
      if (c == '"')
      {
        b.append('"');
        b.append('"');
      }
      else
      {
        b.append(c);
      }
    }
    b.append('"');
    return b.toString();
  }

  private String filter(final String role) throws ReportDataFactoryException
  {
    final Configuration configuration = ClassicEngineBoot.getInstance().getGlobalConfig();
    if ("true".equals(configuration.getConfigProperty(ROLE_FILTER_ENABLE_CONFIG_KEY)) == false)
    {
      return role;
    }

    final Iterator staticDenyKeys = configuration.findPropertyKeys(DENY_ROLE_CONFIG_KEY);
    while (staticDenyKeys.hasNext())
    {
      final String key = (String) staticDenyKeys.next();
      final String value = configuration.getConfigProperty(key);
      if (ObjectUtilities.equal(value, role))
      {
        return null;
      }
    }

    final Iterator regExpDenyKeys = configuration.findPropertyKeys(DENY_REGEXP_CONFIG_KEY);
    while (regExpDenyKeys.hasNext())
    {
      final String key = (String) regExpDenyKeys.next();
      final String value = configuration.getConfigProperty(key);
      try
      {
        if (role.matches(value))
        {
          return null;
        }
      }
      catch (PatternSyntaxException pe)
      {
        throw new ReportDataFactoryException("Unable to match reg-exp role filter:", pe);
      }
    }

    boolean hasAccept = false;
    final Iterator staticAcceptKeys = configuration.findPropertyKeys(ACCEPT_ROLES_CONFIG_KEY);
    while (staticAcceptKeys.hasNext())
    {
      hasAccept = true;
      final String key = (String) staticAcceptKeys.next();
      final String value = configuration.getConfigProperty(key);
      if (ObjectUtilities.equal(value, role))
      {
        return role;
      }
    }

    final Iterator regExpAcceptKeys = configuration.findPropertyKeys(ACCEPT_REGEXP_CONFIG_KEY);
    while (regExpAcceptKeys.hasNext())
    {
      hasAccept = true;
      final String key = (String) regExpAcceptKeys.next();
      final String value = configuration.getConfigProperty(key);
      try
      {
        if (role.matches(value))
        {
          return role;
        }
      }
      catch (PatternSyntaxException pe)
      {
        throw new ReportDataFactoryException("Unable to match reg-exp role filter:", pe);
      }
    }
    if (hasAccept == false)
    {
      return role;
    }
    return null;
  }

  public String translateQuery(final String queryName)
  {
    return queryName;
  }


  public Object computeQueryHash(final String queryRaw, final DataRow parameter) throws ReportDataFactoryException
  {
    final ArrayList<Object> list = new ArrayList<Object>();
    list.add(getClass().getName());
    list.add(queryRaw);
    list.add(getCubeFileProvider().getConnectionHash());
    list.add(getDataSourceProvider().getConnectionHash());
    list.add(getMondrianConnectionProvider().getConnectionHash(computeProperties(parameter)));
    list.add(computeProperties(parameter));
    return list;
  }

  public String[] getParameterFields(final DataRow parameters,
                                     final String rawMdxQuery) throws ReportDataFactoryException
  {
    try
    {
      if (connection == null)
      {
        connection = mondrianConnectionProvider.createConnection(computeProperties(parameters), dataSourceProvider.getDataSource());
      }
    }
    catch (SQLException e)
    {
      throw new ReportDataFactoryException("Failed to create datasource:" + e.getLocalizedMessage(), e);
    }
    catch (MondrianException e)
    {
      throw new ReportDataFactoryException("Failed to create datasource:" + e.getLocalizedMessage(), e);
    }

    try
    {
      if (connection == null)
      {
        throw new ReportDataFactoryException("Factory is closed.");
      }
      final LinkedHashSet<String> parameter = new LinkedHashSet<String>();

      final MDXCompiler compiler = new MDXCompiler(parameters, locale);
      final String mdxQuery = compiler.translateAndLookup(rawMdxQuery, parameters);
      parameter.addAll(compiler.getCollectedParameter());
      // Alternatively, JNDI is possible. Maybe even more ..
      final Query query = connection.parseQuery(mdxQuery);
      final Parameter[] queryParameters = query.getParameters();
      for (int i = 0; i < queryParameters.length; i++)
      {
        final Parameter queryParameter = queryParameters[i];
        parameter.add(queryParameter.getName());
      }
      if (jdbcUserField != null)
      {
        parameter.add(jdbcUserField);
      }
      if (roleField != null)
      {
        parameter.add(roleField);
      }
      parameter.add(DataFactory.QUERY_LIMIT);
      return parameter.toArray(new String[parameter.size()]);
    }
    catch (MondrianException e)
    {
      throw new ReportDataFactoryException("Failed to create datasource:" + e.getLocalizedMessage(), e);
    }
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.extensions.datasources.mondrian.AbstractMDXDataFactory

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.