public SegmentCacheManager(MondrianServer server) {
    this.server = server;
    ACTOR = new Actor();
    thread = new Thread(ACTOR, "mondrian.rolap.agg.SegmentCacheManager$ACTOR");
    thread.setDaemon(true);
    thread.start();

    // Create the index registry.
    this.indexRegistry = new SegmentCacheIndexRegistry();

    // Add a local cache, if needed.
    if (!MondrianProperties.instance().DisableCaching.get()) {
      final MemorySegmentCache cache = new MemorySegmentCache();
      segmentCacheWorkers.add(new SegmentCacheWorker(cache, thread));
    }

    // Add an external cache, if configured.
    final List<SegmentCache> externalCache = SegmentCacheWorker.initCache();
    for (SegmentCache cache : externalCache) {
      // Create a worker for this external cache
      segmentCacheWorkers.add(new SegmentCacheWorker(cache, thread));
      // Hook up a listener so it can update
      // the segment index.
      cache.addListener(new AsyncCacheListener(this, server));
    }

    compositeCache = new CompositeSegmentCache(segmentCacheWorkers);
  }
Beispiel #2
0
  int internalAddRow(SqlStatement stmt, int column) throws SQLException {
    RolapMember member = null;
    if (getCurrMember() != null) {
      member = getCurrMember();
    } else {
      boolean checkCacheStatus = true;
      final List<SqlStatement.Accessor> accessors = stmt.getAccessors();
      for (int i = 0; i <= levelDepth; i++) {
        RolapLevel childLevel = levels[i];
        if (childLevel.isAll()) {
          member = memberBuilder.allMember();
          continue;
        }

        if (childLevel.isParentChild()) {
          column++;
        }

        Object value = accessors.get(column++).get();
        if (value == null) {
          value = RolapUtil.sqlNullValue;
        }
        Object captionValue;
        if (childLevel.hasCaptionColumn()) {
          captionValue = accessors.get(column++).get();
        } else {
          captionValue = null;
        }
        RolapMember parentMember = member;
        Object key = cache.makeKey(parentMember, value);
        member = cache.getMember(key, checkCacheStatus);
        checkCacheStatus = false; /* Only check the first time */
        if (member == null) {
          if (constraint instanceof RolapNativeCrossJoin.NonEmptyCrossJoinConstraint
              && childLevel.isParentChild()) {
            member = castToNonEmptyCJConstraint(constraint).findMember(value);
          }
          if (member == null) {
            member =
                memberBuilder.makeMember(
                    parentMember, childLevel, value, captionValue, parentChild, stmt, key, column);
          }
        }

        // Skip over the columns consumed by makeMember
        if (!childLevel.getOrdinalExp().equals(childLevel.getKeyExp())) {
          ++column;
        }
        column += childLevel.getProperties().length;
      }
      setCurrMember(member);
    }
    getList().add(member);
    return column;
  }
  public void testJndiConnection() throws NamingException {
    // Cannot guarantee that this test will work if they have chosen to
    // resolve data sources other than by JNDI.
    if (MondrianProperties.instance().DataSourceResolverClass.isSet()) {
      return;
    }
    // get a regular connection
    Util.PropertyList properties = TestContext.instance().getConnectionProperties().clone();
    final StringBuilder buf = new StringBuilder();
    final DataSource dataSource = RolapConnection.createDataSource(null, properties, buf);
    // Don't know what the connect string is - it differs with database
    // and with the user's set up - but we know that it contains a JDBC
    // connect string. Best we can do is check that createDataSource is
    // setting it to something.
    final String desc = buf.toString();
    assertTrue(desc, desc.startsWith("Jdbc="));

    final List<String> lookupCalls = new ArrayList<String>();
    // mock the JNDI naming manager to provide that datasource
    THREAD_INITIAL_CONTEXT.set(
        // Use lazy initialization. Otherwise during initialization of this
        // initial context JNDI tries to create a default initial context
        // and bumps into itself coming the other way.
        new InitialContext(true) {
          public Object lookup(String str) {
            lookupCalls.add("Called");
            return dataSource;
          }
        });

    // Use the datasource property to connect to the database.
    // Remove user and password, because some data sources (those using
    // pools) don't allow you to override user.
    Util.PropertyList properties2 = TestContext.instance().getConnectionProperties().clone();
    properties2.remove(RolapConnectionProperties.Jdbc.name());
    properties2.remove(RolapConnectionProperties.JdbcUser.name());
    properties2.remove(RolapConnectionProperties.JdbcPassword.name());
    properties2.put(RolapConnectionProperties.DataSource.name(), "jnditest");
    DriverManager.getConnection(properties2, null);

    // if we've made it here with lookupCalls,
    // we've successfully used JNDI
    assertTrue(lookupCalls.size() > 0);
  }
 public List<SegmentHeader> getSegmentHeaders() {
   // Special case 0 and 1 workers, for which the 'union' operation
   // is trivial.
   switch (workers.size()) {
     case 0:
       return Collections.emptyList();
     case 1:
       return workers.get(0).getSegmentHeaders();
     default:
       final List<SegmentHeader> list = new ArrayList<SegmentHeader>();
       final Set<SegmentHeader> set = new HashSet<SegmentHeader>();
       for (SegmentCacheWorker worker : workers) {
         for (SegmentHeader header : worker.getSegmentHeaders()) {
           if (set.add(header)) {
             list.add(header);
           }
         }
       }
       return list;
   }
 }
 public List<Hierarchy> getHierarchies() {
   if (queryAxis.getAxisOrdinal().isFilter()) {
     // Slicer contains all dimensions not mentioned on other axes.
     // The list contains the default hierarchy of
     // each dimension not already in the slicer or in another axes.
     Set<Dimension> dimensionSet = new HashSet<Dimension>();
     for (CellSetAxisMetaData cellSetAxisMetaData : cellSetMetaData.getAxesMetaData()) {
       for (Hierarchy hierarchy : cellSetAxisMetaData.getHierarchies()) {
         dimensionSet.add(hierarchy.getDimension());
       }
     }
     List<Hierarchy> hierarchyList = new ArrayList<Hierarchy>();
     for (Dimension dimension : cellSetMetaData.getCube().getDimensions()) {
       if (dimensionSet.add(dimension)) {
         hierarchyList.add(dimension.getDefaultHierarchy());
       }
     }
     // In case a dimension has multiple hierarchies, return the
     // declared type of the slicer expression. For example, if the
     // WHERE clause contains [Time].[Weekly].[1997].[Week 6], the
     // slicer should contain [Time].[Weekly] not the default hierarchy
     // [Time].
     for (Hierarchy hierarchy : getHierarchiesNonFilter()) {
       if (hierarchy.getDimension().getHierarchies().size() == 1) {
         continue;
       }
       for (int i = 0; i < hierarchyList.size(); i++) {
         Hierarchy hierarchy1 = hierarchyList.get(i);
         if (hierarchy1.getDimension().equals(hierarchy.getDimension())
             && hierarchy1 != hierarchy) {
           hierarchyList.set(i, hierarchy);
         }
       }
     }
     return hierarchyList;
   } else {
     return getHierarchiesNonFilter();
   }
 }
 /**
  * Returns the hierarchies on a non-filter axis.
  *
  * @return List of hierarchies, never null
  */
 private List<Hierarchy> getHierarchiesNonFilter() {
   final Exp exp = queryAxis.getSet();
   if (exp == null) {
     return Collections.emptyList();
   }
   Type type = exp.getType();
   if (type instanceof SetType) {
     type = ((SetType) type).getElementType();
   }
   final MondrianOlap4jConnection olap4jConnection =
       cellSetMetaData.olap4jStatement.olap4jConnection;
   if (type instanceof TupleType) {
     final TupleType tupleType = (TupleType) type;
     List<Hierarchy> hierarchyList = new ArrayList<Hierarchy>();
     for (Type elementType : tupleType.elementTypes) {
       hierarchyList.add(olap4jConnection.toOlap4j(elementType.getHierarchy()));
     }
     return hierarchyList;
   } else {
     return Collections.singletonList((Hierarchy) olap4jConnection.toOlap4j(type.getHierarchy()));
   }
 }
  /**
   * Creates a MondrianOlap4jCellSetAxisMetaData.
   *
   * @param cellSetMetaData Cell set axis metadata
   * @param queryAxis Query axis
   */
  MondrianOlap4jCellSetAxisMetaData(
      MondrianOlap4jCellSetMetaData cellSetMetaData, QueryAxis queryAxis) {
    if (queryAxis == null) {
      queryAxis =
          new QueryAxis(
              false,
              null,
              AxisOrdinal.StandardAxisOrdinal.SLICER,
              QueryAxis.SubtotalVisibility.Undefined);
    }
    this.queryAxis = queryAxis;
    this.cellSetMetaData = cellSetMetaData;

    // populate property list
    for (Id id : queryAxis.getDimensionProperties()) {
      propertyList.add(Property.StandardMemberProperty.valueOf(id.toStringArray()[0]));
    }
  }
Beispiel #8
0
  public List<RolapMember> close() {
    final boolean asList =
        this.constraint.getEvaluator() != null
            && this.constraint.getEvaluator().getQuery().getResultStyle() == ResultStyle.LIST;
    final int limit = MondrianProperties.instance().ResultLimit.get();

    final List<RolapMember> l =
        new AbstractList<RolapMember>() {
          private boolean moreRows = true;
          private int offset = 0;
          private RolapMember first = null;
          private boolean firstMemberAssigned = false;

          /** Performs a load of the whole result set. */
          public int size() {
            while (this.moreRows) {
              this.moreRows = sqlTupleReader.readNextTuple();
              if (limit > 0 && !asList && getList().size() > limit) {
                System.out.println("Target: 199, Ouch! Toooo big array..." + this.hashCode());
                new Throwable().printStackTrace();
              }
            }

            return getList().size();
          }

          public RolapMember get(final int idx) {
            if (asList) {
              return getList().get(idx);
            }

            if (idx == 0 && this.firstMemberAssigned) {
              return this.first;
            }
            int index = idx - offset;

            if (0 < limit && index < 0) {
              // Cannot send NoSuchElementException since its intercepted
              // by AbstractSequentialList to identify out of bounds.
              throw new RuntimeException("Element " + idx + " has been forgotten");
            }

            while (index >= getList().size() && this.moreRows) {
              this.moreRows = sqlTupleReader.readNextTuple();
              if (limit > 0 && getList().size() > limit) {
                while (getList().size() > limit) {
                  index--;
                  offset++;
                  ((LinkedList) getList()).removeFirst();
                }
              }
            }

            if (idx == 0) {
              this.first = getList().get(index);

              // Above might run into exception which is caught in
              // isEmpty(). So can change the state of the object after
              // that.
              this.firstMemberAssigned = true;
              return this.first;
            } else {
              return getList().get(index);
            }
          }

          public RolapMember set(final int i, final RolapMember e) {
            if (asList) {
              return getList().set(i, e);
            } else {
              throw new UnsupportedOperationException();
            }
          }

          public boolean isEmpty() {
            try {
              get(0);
              return false;
            } catch (IndexOutOfBoundsException e) {
              return true;
            }
          }

          public int hashCode() {
            return Target.this.hashCode();
          }

          public Iterator<RolapMember> iterator() {
            return new Iterator<RolapMember>() {
              private int cursor = 0;

              public boolean hasNext() {
                try {
                  get(cursor);
                  return true;
                } catch (IndexOutOfBoundsException ioobe) {
                  return false;
                }
              }

              public RolapMember next() {
                return get(cursor++);
              }

              public void remove() {
                throw new UnsupportedOperationException();
              }
            };
          }
        };

    if (asList) {
      l.size();
    }

    return l;
  }
    public FlushResult call() throws Exception {
      // For each measure and each star, ask the index
      // which headers intersect.
      final List<SegmentHeader> headers = new ArrayList<SegmentHeader>();
      final List<Member> measures = CacheControlImpl.findMeasures(region);
      final SegmentColumn[] flushRegion = CacheControlImpl.findAxisValues(region);
      final List<RolapStar> starList = CacheControlImpl.getStarList(region);

      for (Member member : measures) {
        if (!(member instanceof RolapStoredMeasure)) {
          continue;
        }
        final RolapStoredMeasure storedMeasure = (RolapStoredMeasure) member;
        final RolapStar star = storedMeasure.getCube().getStar();
        final SegmentCacheIndex index = cacheMgr.indexRegistry.getIndex(star);
        headers.addAll(
            index.intersectRegion(
                member.getDimension().getSchema().getName(),
                ((RolapSchema) member.getDimension().getSchema()).getChecksum(),
                storedMeasure.getCube().getName(),
                storedMeasure.getName(),
                storedMeasure.getCube().getStar().getFactTable().getAlias(),
                flushRegion));
      }

      // If flushRegion is empty, this means we must clear all
      // segments for the region's measures.
      if (flushRegion.length == 0) {
        for (final SegmentHeader header : headers) {
          for (RolapStar star : starList) {
            cacheMgr.indexRegistry.getIndex(star).remove(header);
          }

          // Remove the segment from external caches. Use an
          // executor, because it may take some time. We discard
          // the future, because we don't care too much if it fails.
          cacheControlImpl.trace(
              "discard segment - it cannot be constrained and maintain consistency:\n"
                  + header.getDescription());

          final Future<?> task =
              cacheMgr.cacheExecutor.submit(
                  new Runnable() {
                    public void run() {
                      try {
                        // Note that the SegmentCache API doesn't
                        // require us to verify that the segment
                        // exists (by calling "contains") before we
                        // call "remove".
                        cacheMgr.compositeCache.remove(header);
                      } catch (Throwable e) {
                        LOGGER.warn("remove header failed: " + header, e);
                      }
                    }
                  });
          Util.safeGet(task, "SegmentCacheManager.flush");
        }
        return new FlushResult(Collections.<Callable<Boolean>>emptyList());
      }

      // Now we know which headers intersect. For each of them,
      // we append an excluded region.
      //
      // TODO: Optimize the logic here. If a segment is mostly
      // empty, we should trash it completely.
      final List<Callable<Boolean>> callableList = new ArrayList<Callable<Boolean>>();
      for (final SegmentHeader header : headers) {
        if (!header.canConstrain(flushRegion)) {
          // We have to delete that segment altogether.
          cacheControlImpl.trace(
              "discard segment - it cannot be constrained and maintain consistency:\n"
                  + header.getDescription());
          for (RolapStar star : starList) {
            cacheMgr.indexRegistry.getIndex(star).remove(header);
          }
          continue;
        }
        final SegmentHeader newHeader = header.constrain(flushRegion);
        for (final SegmentCacheWorker worker : cacheMgr.segmentCacheWorkers) {
          callableList.add(
              new Callable<Boolean>() {
                public Boolean call() throws Exception {
                  boolean existed;
                  if (worker.supportsRichIndex()) {
                    final SegmentBody sb = worker.get(header);
                    existed = worker.remove(header);
                    if (sb != null) {
                      worker.put(newHeader, sb);
                    }
                  } else {
                    // The cache doesn't support rich index. We
                    // have to clear the segment entirely.
                    existed = worker.remove(header);
                  }
                  return existed;
                }
              });
        }
        for (RolapStar star : starList) {
          SegmentCacheIndex index = cacheMgr.indexRegistry.getIndex(star);
          index.remove(header);
          index.add(newHeader, false, null);
        }
      }

      // Done
      return new FlushResult(callableList);
    }