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); }
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])); } }
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); }