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