001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2006-2013 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.server;
011
012import mondrian.olap.MondrianException;
013import mondrian.olap.MondrianServer;
014import mondrian.olap4j.CatalogFinder;
015import mondrian.resource.MondrianResource;
016import mondrian.rolap.RolapConnection;
017import mondrian.rolap.RolapResultShepherd;
018import mondrian.rolap.RolapSchema;
019import mondrian.rolap.agg.AggregationManager;
020import mondrian.server.monitor.*;
021import mondrian.spi.CatalogLocator;
022import mondrian.util.LockBox;
023import mondrian.xmla.XmlaHandler;
024
025import org.apache.commons.collections.map.ReferenceMap;
026import org.apache.log4j.Logger;
027
028import org.olap4j.OlapConnection;
029
030import java.sql.SQLException;
031import java.util.*;
032import java.util.concurrent.atomic.AtomicInteger;
033
034/**
035 * Implementation of {@link mondrian.olap.MondrianServer}.
036 *
037 * @author jhyde
038 * @since Jun 25, 2006
039 */
040class MondrianServerImpl
041    extends MondrianServer
042    implements CatalogFinder, XmlaHandler.ConnectionFactory
043{
044    /**
045     * Id of server. Unique within JVM's lifetime. Not the same as the ID of
046     * the server within a lockbox.
047     */
048    private final int id = ID_GENERATOR.incrementAndGet();
049
050    /**
051     * Within a server, registry of objects such as data sources and roles.
052     * For convenience, all servers currently share the same lockbox.
053     */
054    private final LockBox lockBox;
055
056    private final Repository repository;
057
058    private final CatalogLocator catalogLocator;
059
060    private final RolapResultShepherd shepherd;
061
062    /**
063     * Map of open connections, by id. Connections are added just after
064     * construction, and are removed when they call close. Garbage collection
065     * may cause a connection to be removed earlier.
066     */
067    @SuppressWarnings("unchecked")
068    private final Map<Integer, RolapConnection> connectionMap =
069         // We use a reference map here because the value
070         // is what needs to be week, not the key, as it
071         // would be the case with a WeakHashMap.
072        Collections.synchronizedMap(
073            new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
074
075    /**
076     * Map of open statements, by id. Statements are added just after
077     * construction, and are removed when they call close. Garbage collection
078     * may cause a connection to be removed earlier.
079     */
080    @SuppressWarnings("unchecked")
081    private final Map<Long, Statement> statementMap =
082         // We use a reference map here because the value
083         // is what needs to be week, not the key, as it
084         // would be the case with a WeakHashMap.
085        Collections.synchronizedMap(
086            new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
087
088    private final MonitorImpl monitor = new MonitorImpl();
089
090    private final AggregationManager aggMgr;
091
092    private boolean shutdown = false;
093
094    private static final Logger LOGGER =
095        Logger.getLogger(MondrianServerImpl.class);
096
097    private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
098
099    private static final List<String> KEYWORD_LIST =
100        Collections.unmodifiableList(Arrays.asList(
101            "$AdjustedProbability", "$Distance", "$Probability",
102            "$ProbabilityStDev", "$ProbabilityStdDeV", "$ProbabilityVariance",
103            "$StDev", "$StdDeV", "$Support", "$Variance",
104            "AddCalculatedMembers", "Action", "After", "Aggregate", "All",
105            "Alter", "Ancestor", "And", "Append", "As", "ASC", "Axis",
106            "Automatic", "Back_Color", "BASC", "BDESC", "Before",
107            "Before_And_After", "Before_And_Self", "Before_Self_After",
108            "BottomCount", "BottomPercent", "BottomSum", "Break", "Boolean",
109            "Cache", "Calculated", "Call", "Case", "Catalog_Name", "Cell",
110            "Cell_Ordinal", "Cells", "Chapters", "Children",
111            "Children_Cardinality", "ClosingPeriod", "Cluster",
112            "ClusterDistance", "ClusterProbability", "Clusters",
113            "CoalesceEmpty", "Column_Values", "Columns", "Content",
114            "Contingent", "Continuous", "Correlation", "Cousin", "Covariance",
115            "CovarianceN", "Create", "CreatePropertySet", "CrossJoin", "Cube",
116            "Cube_Name", "CurrentMember", "CurrentCube", "Custom", "Cyclical",
117            "DefaultMember", "Default_Member", "DESC", "Descendents",
118            "Description", "Dimension", "Dimension_Unique_Name", "Dimensions",
119            "Discrete", "Discretized", "DrillDownLevel",
120            "DrillDownLevelBottom", "DrillDownLevelTop", "DrillDownMember",
121            "DrillDownMemberBottom", "DrillDownMemberTop", "DrillTrough",
122            "DrillUpLevel", "DrillUpMember", "Drop", "Else", "Empty", "End",
123            "Equal_Areas", "Exclude_Null", "ExcludeEmpty", "Exclusive",
124            "Expression", "Filter", "FirstChild", "FirstRowset",
125            "FirstSibling", "Flattened", "Font_Flags", "Font_Name",
126            "Font_size", "Fore_Color", "Format_String", "Formatted_Value",
127            "Formula", "From", "Generate", "Global", "Head", "Hierarchize",
128            "Hierarchy", "Hierary_Unique_name", "IIF", "IsEmpty",
129            "Include_Null", "Include_Statistics", "Inclusive", "Input_Only",
130            "IsDescendant", "Item", "Lag", "LastChild", "LastPeriods",
131            "LastSibling", "Lead", "Level", "Level_Unique_Name", "Levels",
132            "LinRegIntercept", "LinRegR2", "LinRegPoint", "LinRegSlope",
133            "LinRegVariance", "Long", "MaxRows", "Median", "Member",
134            "Member_Caption", "Member_Guid", "Member_Name", "Member_Ordinal",
135            "Member_Type", "Member_Unique_Name", "Members",
136            "Microsoft_Clustering", "Microsoft_Decision_Trees", "Mining",
137            "Model", "Model_Existence_Only", "Models", "Move", "MTD", "Name",
138            "Nest", "NextMember", "Non", "Normal", "Not", "Ntext", "Nvarchar",
139            "OLAP", "On", "OpeningPeriod", "OpenQuery", "Or", "Ordered",
140            "Ordinal", "Pages", "Pages", "ParallelPeriod", "Parent",
141            "Parent_Level", "Parent_Unique_Name", "PeriodsToDate", "PMML",
142            "Predict", "Predict_Only", "PredictAdjustedProbability",
143            "PredictHistogram", "Prediction", "PredictionScore",
144            "PredictProbability", "PredictProbabilityStDev",
145            "PredictProbabilityVariance", "PredictStDev", "PredictSupport",
146            "PredictVariance", "PrevMember", "Probability",
147            "Probability_StDev", "Probability_StdDev", "Probability_Variance",
148            "Properties", "Property", "QTD", "RangeMax", "RangeMid",
149            "RangeMin", "Rank", "Recursive", "Refresh", "Related", "Rename",
150            "Rollup", "Rows", "Schema_Name", "Sections", "Select", "Self",
151            "Self_And_After", "Sequence_Time", "Server", "Session", "Set",
152            "SetToArray", "SetToStr", "Shape", "Skip", "Solve_Order", "Sort",
153            "StdDev", "Stdev", "StripCalculatedMembers", "StrToSet",
154            "StrToTuple", "SubSet", "Support", "Tail", "Text", "Thresholds",
155            "ToggleDrillState", "TopCount", "TopPercent", "TopSum",
156            "TupleToStr", "Under", "Uniform", "UniqueName", "Use", "Value",
157            "Value", "Var", "Variance", "VarP", "VarianceP", "VisualTotals",
158            "When", "Where", "With", "WTD", "Xor"));
159
160    /**
161     * Creates a MondrianServerImpl.
162     *
163     * @param registry Registry of all servers in this JVM
164     * @param repository Repository of catalogs and schemas
165     * @param catalogLocator Catalog locator
166     */
167    MondrianServerImpl(
168        MondrianServerRegistry registry,
169        Repository repository,
170        CatalogLocator catalogLocator)
171    {
172        assert repository != null;
173        assert catalogLocator != null;
174        this.repository = repository;
175        this.catalogLocator = catalogLocator;
176
177        // All servers in a JVM share the same lockbox. This is a bit more
178        // forgiving to applications which have slightly mismatched
179        // specifications of the servers where they create and retrieve the
180        // entry.
181        this.lockBox = registry.lockBox;
182
183        this.aggMgr = new AggregationManager(this);
184
185        this.shepherd = new RolapResultShepherd();
186    }
187
188    @Override
189    protected void finalize() throws Throwable {
190        try {
191            super.finalize();
192            shutdown(true);
193        } catch (Throwable t) {
194            LOGGER.info(
195                MondrianResource.instance()
196                    .FinalizerErrorMondrianServerImpl.baseMessage,
197                t);
198        }
199    }
200
201    public int getId() {
202        return id;
203    }
204
205    @Override
206    public RolapResultShepherd getResultShepherd() {
207        if (shutdown) {
208            throw new MondrianException("Server already shutdown.");
209        }
210        return this.shepherd;
211    }
212
213    public List<String> getKeywords() {
214        return KEYWORD_LIST;
215    }
216
217    public LockBox getLockBox() {
218        return lockBox;
219    }
220
221    public AggregationManager getAggregationManager() {
222        if (shutdown) {
223            throw new MondrianException("Server already shutdown.");
224        }
225        return aggMgr;
226    }
227
228    @Override
229    public OlapConnection getConnection(
230        String databaseName,
231        String catalogName,
232        String roleName)
233        throws SQLException
234    {
235        if (shutdown) {
236            throw new MondrianException("Server already shutdown.");
237        }
238        return this.getConnection(
239            databaseName, catalogName, roleName,
240            new Properties());
241    }
242
243    @Override
244    public OlapConnection getConnection(
245        String databaseName,
246        String catalogName,
247        String roleName,
248        Properties props)
249        throws SQLException
250    {
251        if (shutdown) {
252            throw new MondrianException("Server already shutdown.");
253        }
254        return repository.getConnection(
255            this, databaseName, catalogName, roleName, props);
256    }
257
258    public List<String> getCatalogNames(
259        RolapConnection connection)
260    {
261        if (shutdown) {
262            throw new MondrianException("Server already shutdown.");
263        }
264        return
265            repository.getCatalogNames(
266                connection,
267                // We assume that Mondrian supports a single database
268                // per server.
269                repository.getDatabaseNames(connection).get(0));
270    }
271
272    public List<Map<String, Object>> getDatabases(
273        RolapConnection connection)
274    {
275        if (shutdown) {
276            throw new MondrianException("Server already shutdown.");
277        }
278        return repository.getDatabases(connection);
279    }
280
281    @Override
282    public CatalogLocator getCatalogLocator() {
283        if (shutdown) {
284            throw new MondrianException("Server already shutdown.");
285        }
286        return catalogLocator;
287    }
288
289    @Override
290    public void shutdown() {
291        this.shutdown(false);
292    }
293
294    private void shutdown(boolean silent) {
295        if (this == MondrianServerRegistry.INSTANCE.staticServer) {
296            LOGGER.warn("Can't shutdown the static server.");
297            return;
298        }
299        if (shutdown) {
300            if (silent) {
301                return;
302            }
303            throw new MondrianException("Server already shutdown.");
304        }
305        this.shutdown  = true;
306        aggMgr.shutdown();
307        monitor.shutdown();
308        repository.shutdown();
309        shepherd.shutdown();
310    }
311
312    @Override
313    synchronized public void addConnection(RolapConnection connection) {
314        if (shutdown) {
315            throw new MondrianException("Server already shutdown.");
316        }
317        connectionMap.put(
318            connection.getId(),
319            connection);
320        monitor.sendEvent(
321            new ConnectionStartEvent(
322                System.currentTimeMillis(),
323                connection.getServer().getId(),
324                connection.getId()));
325    }
326
327    @Override
328    synchronized public void removeConnection(RolapConnection connection) {
329        if (shutdown) {
330            throw new MondrianException("Server already shutdown.");
331        }
332        connectionMap.remove(connection.getId());
333        monitor.sendEvent(
334            new ConnectionEndEvent(
335                System.currentTimeMillis(),
336                getId(),
337                connection.getId()));
338    }
339
340    @Override
341    public RolapConnection getConnection(int connectionId) {
342        if (shutdown) {
343            throw new MondrianException("Server already shutdown.");
344        }
345        return connectionMap.get(connectionId);
346    }
347
348    @Override
349    synchronized public void addStatement(Statement statement) {
350        if (shutdown) {
351            throw new MondrianException("Server already shutdown.");
352        }
353        statementMap.put(
354            statement.getId(),
355            statement);
356        final RolapConnection connection =
357            statement.getMondrianConnection();
358        monitor.sendEvent(
359            new StatementStartEvent(
360                System.currentTimeMillis(),
361                connection.getServer().getId(),
362                connection.getId(),
363                statement.getId()));
364    }
365
366    @Override
367    synchronized public void removeStatement(Statement statement) {
368        if (shutdown) {
369            throw new MondrianException("Server already shutdown.");
370        }
371        statementMap.remove(statement.getId());
372        final RolapConnection connection =
373            statement.getMondrianConnection();
374        monitor.sendEvent(
375            new StatementEndEvent(
376                System.currentTimeMillis(),
377                connection.getServer().getId(),
378                connection.getId(),
379                statement.getId()));
380    }
381
382    public Monitor getMonitor() {
383        if (shutdown) {
384            throw new MondrianException("Server already shutdown.");
385        }
386        return monitor;
387    }
388
389    public Map<String, RolapSchema> getRolapSchemas(
390        RolapConnection connection,
391        String catalogName)
392    {
393        if (shutdown) {
394            throw new MondrianException("Server already shutdown.");
395        }
396        return
397            repository.getRolapSchemas(
398                connection,
399                // We assume that Mondrian supports a single database
400                // per server.
401                repository.getDatabaseNames(connection).get(0),
402                catalogName);
403    }
404
405    public Map<String, Object> getPreConfiguredDiscoverDatasourcesResponse() {
406        // No pre-configured response; XMLA servlet will connect to get
407        // data source info.
408        return null;
409    }
410}
411
412// End MondrianServerImpl.java