KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > aggmatcher > AggTableManager


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/AggTableManager.java#24 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2005-2007 Julian Hyde and others
7 // All Rights Reserved.
8 // You must accept the terms of that agreement to use this software.
9 */

10
11 package mondrian.rolap.aggmatcher;
12
13 import mondrian.olap.*;
14 import mondrian.rolap.*;
15 import mondrian.recorder.*;
16 import mondrian.resource.MondrianResource;
17
18 import org.apache.log4j.Logger;
19 import org.eigenbase.util.property.*;
20 import org.eigenbase.util.property.Property;
21
22 import javax.sql.DataSource JavaDoc;
23 import java.util.*;
24 import java.sql.SQLException JavaDoc;
25
26 /**
27  * Manages aggregate tables.
28  *
29  * <p>It is used as follows:<ul>
30  * <li>A {@link mondrian.rolap.RolapSchema} creates an {@link AggTableManager},
31  * and stores it in a member variable to ensure that it is not
32  * garbage-collected.
33  * <li>The {@link AggTableManager} creates and registers
34  * {@link org.eigenbase.util.property.Trigger} objects, so that it is notified
35  * when properties pertinent to aggregate tables change.
36  * <li>The {@link mondrian.rolap.RolapSchema} calls {@link #initialize()},
37  * which scans the JDBC catalog and identifies aggregate tables.
38  * <li>For each aggregate table, it creates an {@link AggStar} and calls
39  * {@link RolapStar#addAggStar(AggStar)}.
40  *
41  * @author Richard M. Emberson
42  * @version $Id: //open/mondrian/src/main/mondrian/rolap/aggmatcher/AggTableManager.java#24 $
43  */

44 public class AggTableManager {
45     private static final Logger LOGGER =
46             Logger.getLogger(AggTableManager.class);
47
48     private final RolapSchema schema;
49
50     private static final MondrianResource mres = MondrianResource.instance();
51
52     /**
53      * This is used to create forward references to triggers (so they do not
54      * get reaped until the RolapSchema is reaped).
55      */

56     private Trigger[] triggers;
57
58     public AggTableManager(final RolapSchema schema) {
59         this.schema = schema;
60     }
61
62     /**
63      * This should ONLY be called if the AggTableManager is no longer going
64      * to be used. In fact, it should only be called indirectly by its
65      * associated RolapSchema object.
66      */

67     public void finalCleanUp() {
68         removeJdbcSchema();
69         deregisterTriggers(MondrianProperties.instance());
70
71         if (getLogger().isDebugEnabled()) {
72             StringBuilder JavaDoc buf = new StringBuilder JavaDoc(100);
73             buf.append("AggTableManager.finalCleanUp: schema=");
74             buf.append(schema.getName());
75             getLogger().debug(buf.toString());
76         }
77     }
78
79     /**
80      * Get the Logger.
81      */

82     public Logger getLogger() {
83         return LOGGER;
84     }
85
86     /**
87      * Initializes this object, loading all aggregate tables and associating
88      * them with {@link RolapStar}s.
89      * This method should only be called once.
90      */

91     public void initialize() {
92         if (MondrianProperties.instance().ReadAggregates.get()) {
93             try {
94                 loadRolapStarAggregates();
95
96             } catch (SQLException JavaDoc ex) {
97                 throw mres.AggLoadingError.ex(ex);
98             }
99         }
100         registerTriggers();
101         printResults();
102     }
103     private void printResults() {
104 /*
105  * This was too much information at the INFO level, compared to the
106  * rest of Mondrian
107  *
108  * if (getLogger().isInfoEnabled()) {
109             // print just Star table alias and AggStar table names
110             StringBuilder buf = new StringBuilder(1024);
111             buf.append(Util.nl);
112             for (Iterator it = getStars(); it.hasNext(); ) {
113                 RolapStar star = (RolapStar) it.next();
114                 buf.append(star.getFactTable().getAlias());
115                 buf.append(Util.nl);
116                 for (Iterator ait = star.getAggStars(); ait.hasNext(); ) {
117                     AggStar aggStar = (AggStar) ait.next();
118                     buf.append(" ");
119                     buf.append(aggStar.getFactTable().getName());
120                     buf.append(Util.nl);
121                 }
122             }
123             getLogger().info(buf.toString());
124
125         } else
126 */

127         if (getLogger().isDebugEnabled()) {
128             // print everything, Star, subTables, AggStar and subTables
129
// could be a lot
130
StringBuilder JavaDoc buf = new StringBuilder JavaDoc(4096);
131             buf.append(Util.nl);
132             for (RolapStar star : getStars()) {
133                 buf.append(star.toString());
134                 buf.append(Util.nl);
135             }
136             getLogger().debug(buf.toString());
137         }
138     }
139     private void reLoadRolapStarAggregates() {
140         if (MondrianProperties.instance().ReadAggregates.get()) {
141             try {
142                 clearJdbcSchema();
143                 loadRolapStarAggregates();
144                 printResults();
145
146             } catch (SQLException JavaDoc ex) {
147                 throw mres.AggLoadingError.ex(ex);
148             }
149         }
150     }
151
152     private JdbcSchema getJdbcSchema() {
153         DataSource JavaDoc dataSource = schema.getInternalConnection().getDataSource();
154
155         // This actually just does a lookup or simple constructor invocation,
156
// its not expected to fail
157
return JdbcSchema.makeDB(dataSource);
158     }
159
160     /**
161      * Clear the possibly already loaded snapshot of what is in the database.
162      */

163     private void clearJdbcSchema() {
164         DataSource JavaDoc dataSource = schema.getInternalConnection().getDataSource();
165         JdbcSchema.clearDB(dataSource);
166     }
167
168     /**
169      * Remove the possibly already loaded snapshot of what is in the database.
170      */

171     private void removeJdbcSchema() {
172         DataSource JavaDoc dataSource = schema.getInternalConnection().getDataSource();
173         JdbcSchema.removeDB(dataSource);
174     }
175
176
177     /**
178      * This method loads and/or reloads the aggregate tables.
179      * <p>
180      * NOTE: At this point all RolapStars have been made for this
181      * schema (except for dynamically added cubes which I am going
182      * to ignore for right now). So, All stars have their columns
183      * and their BitKeys can be generated.
184      *
185      * @throws SQLException
186      */

187     private void loadRolapStarAggregates() throws SQLException JavaDoc {
188         ListRecorder msgRecorder = new ListRecorder();
189         try {
190
191         DefaultRules rules = DefaultRules.getInstance();
192         JdbcSchema db = getJdbcSchema();
193         // loads tables, not their columns
194
db.load();
195
196         loop:
197         for (RolapStar star : getStars()) {
198             // This removes any AggStars from any previous invocation of this
199
// method (if any)
200
star.prepareToLoadAggregates();
201
202             List<ExplicitRules.Group> aggGroups = getAggGroups(star);
203             for (ExplicitRules.Group group : aggGroups) {
204                 group.validate(msgRecorder);
205             }
206
207
208             String JavaDoc factTableName = star.getFactTable().getAlias();
209
210             JdbcSchema.Table dbFactTable = db.getTable(factTableName);
211             if (dbFactTable == null) {
212                 msgRecorder.reportWarning("No Table found for fact name="
213                     +factTableName);
214
215                 continue loop;
216             }
217
218             // For each column in the dbFactTable, figure out it they are
219
// measure or foreign key columns
220
bindToStar(dbFactTable, star, msgRecorder);
221             String JavaDoc schema = dbFactTable.table.schema;
222
223             // Now look at all tables in the database and per table, first see
224
// if it is a match for an aggregate table for this fact table and
225
// second see if its columns match foreign key and level columns.
226
for (JdbcSchema.Table dbTable : db.getTables()) {
227                 String JavaDoc name = dbTable.getName();
228
229                 // Do the catalog schema aggregate excludes, exclude this
230
// table name.
231
if (ExplicitRules.excludeTable(name, aggGroups)) {
232                     continue;
233                 }
234
235                 //
236
// First see if there is an ExplicitRules match. If so, then if all
237
// of the columns match up, then make an AggStar.
238
// On the other hand, if there is no ExplicitRules match, see if
239
// there is a Default match. If so and if all the columns
240
// match up, then also make an AggStar.
241
//
242
ExplicitRules.TableDef tableDef =
243                     ExplicitRules.getIncludeByTableDef(name, aggGroups);
244
245                 boolean makeAggStar = false;
246                 // Is it handled by the ExplicitRules
247
if (tableDef != null) {
248                     // load columns
249
dbTable.load();
250                     makeAggStar = tableDef.columnsOK(star,
251                                     dbFactTable,
252                                     dbTable,
253                                     msgRecorder);
254                 }
255                 if (! makeAggStar) {
256                     // Is it handled by the DefaultRules
257
if (rules.matchesTableName(factTableName, name)) {
258                         // load columns
259
dbTable.load();
260                         makeAggStar = rules.columnsOK(star,
261                                             dbFactTable,
262                                             dbTable,
263                                             msgRecorder);
264                     }
265                 }
266
267
268                 if (makeAggStar) {
269                     dbTable.setTableUsageType(JdbcSchema.TableUsageType.AGG);
270                     String JavaDoc alias = null;
271                     dbTable.table = new MondrianDef.Table(schema,
272                                                           name,
273                                                           alias);
274                     AggStar aggStar = AggStar.makeAggStar(star,
275                                                           dbTable,
276                                                           msgRecorder);
277                     if (aggStar.getSize() > 0) {
278                         star.addAggStar(aggStar);
279                     } else {
280                         String JavaDoc msg = mres.AggTableZeroSize.str(
281                             aggStar.getFactTable().getName(),
282                             factTableName);
283                         getLogger().warn(msg);
284                     }
285                 }
286                 // Note: if the dbTable name matches but the columnsOK does
287
// not, then this is an error and the aggregate tables
288
// can not be loaded.
289
// We do not "reset" the column usages in the dbTable allowing
290
// it maybe to match another rule.
291
}
292         }
293
294         } catch (RecorderException ex) {
295             throw new MondrianException(ex);
296
297         } finally {
298             msgRecorder.logInfoMessage(getLogger());
299             msgRecorder.logWarningMessage(getLogger());
300             msgRecorder.logErrorMessage(getLogger());
301             if (msgRecorder.hasErrors()) {
302                 throw mres.AggLoadingExceededErrorCount.ex(
303                     msgRecorder.getErrorCount());
304             }
305         }
306     }
307     private boolean runTrigger() {
308         if (RolapSchema.cacheContains(schema)) {
309             return true;
310         } else {
311             // must remove triggers
312
deregisterTriggers(MondrianProperties.instance());
313
314             return false;
315         }
316
317     }
318
319     /**
320      * Registers triggers for the following properties:
321      * <ul>
322      * <li>{@link MondrianProperties#ChooseAggregateByVolume}
323      * <li>{@link MondrianProperties#AggregateRules}
324      * <li>{@link MondrianProperties#AggregateRuleTag}
325      * <li>{@link MondrianProperties#ReadAggregates}
326      * </ul>
327      */

328     private void registerTriggers() {
329         final MondrianProperties properties = MondrianProperties.instance();
330         triggers = new Trigger[] {
331
332             // When the ordering AggStars property is changed, we must
333
// reorder them, so we create a trigger.
334
// There is no need to provide equals/hashCode methods for this
335
// Trigger since it is never explicitly removed.
336
new Trigger() {
337                 public boolean isPersistent() {
338                     return false;
339                 }
340                 public int phase() {
341                     return Trigger.SECONDARY_PHASE;
342                 }
343                 public void execute(Property property, String JavaDoc value) {
344                     if (AggTableManager.this.runTrigger()) {
345                         reOrderAggStarList();
346                     }
347                 }
348             },
349
350             // Register to know when the Default resource/url has changed
351
// so that the default aggregate table recognition rules can
352
// be re-loaded.
353
// There is no need to provide equals/hashCode methods for this
354
// Trigger since it is never explicitly removed.
355
new Trigger() {
356                 public boolean isPersistent() {
357                     return false;
358                 }
359                 public int phase() {
360                     return Trigger.SECONDARY_PHASE;
361                 }
362                 public void execute(Property property, String JavaDoc value) {
363                     if (AggTableManager.this.runTrigger()) {
364                         reLoadRolapStarAggregates();
365                     }
366                 }
367             },
368
369             // If the system started not using aggregates, i.e., the aggregate
370
// tables were not loaded, but then the property
371
// was changed to use aggregates, we must then load the aggregates
372
// if they were never loaded.
373
new Trigger() {
374                 public boolean isPersistent() {
375                     return false;
376                 }
377                 public int phase() {
378                     return Trigger.SECONDARY_PHASE;
379                 }
380                 public void execute(Property property, String JavaDoc value) {
381                     if (AggTableManager.this.runTrigger()) {
382                         reLoadRolapStarAggregates();
383                     }
384                 }
385             }
386         };
387
388         // Note that for each AggTableManager theses triggers are
389
// added to the properties object. Each trigger has just
390
// been created and "knows" its AggTableManager instance.
391
// The triggers' hashCode and equals methods (those provided
392
// by the Object class) are used when removing the trigger.
393
properties.ChooseAggregateByVolume.addTrigger(triggers[0]);
394         properties.AggregateRules.addTrigger(triggers[1]);
395         properties.AggregateRuleTag.addTrigger(triggers[1]);
396         properties.ReadAggregates.addTrigger(triggers[2]);
397     }
398
399     private void deregisterTriggers(final MondrianProperties properties) {
400         // Remove this AggTableManager's instance's triggers.
401
properties.ChooseAggregateByVolume.removeTrigger(triggers[0]);
402         properties.AggregateRules.removeTrigger(triggers[1]);
403         properties.AggregateRuleTag.removeTrigger(triggers[1]);
404         properties.ReadAggregates.removeTrigger(triggers[2]);
405     }
406
407     private Collection<RolapStar> getStars() {
408         return schema.getStars();
409     }
410
411     private void reOrderAggStarList() {
412         for (RolapStar star : getStars()) {
413             star.reOrderAggStarList();
414         }
415     }
416
417     /**
418      * Returns a list containing every
419      * {@link mondrian.rolap.aggmatcher.ExplicitRules.Group} in every
420      * cubes in a given {@link RolapStar}.
421      */

422     protected List<ExplicitRules.Group> getAggGroups(RolapStar star) {
423         List<ExplicitRules.Group> aggGroups =
424             new ArrayList<ExplicitRules.Group>();
425         for (RolapCube cube : schema.getCubesWithStar(star)) {
426             if (cube.hasAggGroup() && cube.getAggGroup().hasRules()) {
427                 aggGroups.add(cube.getAggGroup());
428             }
429         }
430         return aggGroups;
431     }
432
433     /**
434      * This method mines the RolapStar and annotes the JdbcSchema.Table
435      * dbFactTable by creating JdbcSchema.Table.Column.Usage instances. For
436      * example, a measure in the RolapStar becomes a measure usage for the
437      * column with the same name and a RolapStar foreign key column becomes a
438      * foreign key usage for the column with the same name.
439      *
440      * @param dbFactTable
441      * @param star
442      * @param msgRecorder
443      */

444     void bindToStar(final JdbcSchema.Table dbFactTable,
445                     final RolapStar star,
446                     final MessageRecorder msgRecorder) throws SQLException JavaDoc {
447         msgRecorder.pushContextName("AggTableManager.bindToStar");
448         try {
449             // load columns
450
dbFactTable.load();
451
452             dbFactTable.setTableUsageType(JdbcSchema.TableUsageType.FACT);
453
454             MondrianDef.Relation relation = star.getFactTable().getRelation();
455             String JavaDoc schema = null;
456             if (relation instanceof MondrianDef.Table) {
457                 schema = ((MondrianDef.Table) relation).schema;
458             }
459             String JavaDoc tableName = dbFactTable.getName();
460             String JavaDoc alias = null;
461             dbFactTable.table = new MondrianDef.Table(schema, tableName, alias);
462
463             for (JdbcSchema.Table.Column factColumn : dbFactTable.getColumns()) {
464                 String JavaDoc cname = factColumn.getName();
465                 RolapStar.Column[] rcs =
466                     star.getFactTable().lookupColumns(cname);
467
468                 for (RolapStar.Column rc : rcs) {
469                     // its a measure
470
if (rc instanceof RolapStar.Measure) {
471                         RolapStar.Measure rm = (RolapStar.Measure) rc;
472                         JdbcSchema.Table.Column.Usage usage =
473                             factColumn.newUsage(JdbcSchema.UsageType.MEASURE);
474                         usage.setSymbolicName(rm.getName());
475
476                         usage.setAggregator(rm.getAggregator());
477                         usage.rMeasure = rm;
478                     }
479                 }
480
481                 // it still might be a foreign key
482
RolapStar.Table rTable =
483                     star.getFactTable().findTableWithLeftJoinCondition(cname);
484                 if (rTable != null) {
485                     JdbcSchema.Table.Column.Usage usage =
486                         factColumn.newUsage(JdbcSchema.UsageType.FOREIGN_KEY);
487                     usage.setSymbolicName("FOREIGN_KEY");
488                     usage.rTable = rTable;
489                 } else {
490                     RolapStar.Column rColumn =
491                         star.getFactTable().lookupColumn(cname);
492                     if ((rColumn != null) &&
493                         !(rColumn instanceof RolapStar.Measure)) {
494                         // Ok, maybe its used in a non-shared dimension
495
// This is a column in the fact table which is
496
// (not necessarily) a measure but is also not
497
// a foreign key to an external dimension table.
498
JdbcSchema.Table.Column.Usage usage =
499                             factColumn.newUsage(JdbcSchema.UsageType.FOREIGN_KEY);
500                         usage.setSymbolicName("FOREIGN_KEY");
501                         usage.rColumn = rColumn;
502                     }
503                 }
504
505                 // warn if it has not been identified
506
if (!factColumn.hasUsage() && getLogger().isDebugEnabled()) {
507                     String JavaDoc msg = mres.UnknownFactTableColumn.str(
508                         msgRecorder.getContext(),
509                         dbFactTable.getName(),
510                         factColumn.getName());
511                     getLogger().debug(msg);
512                 }
513             }
514         } finally {
515             msgRecorder.popContextName();
516         }
517     }
518 }
519
520 // End AggTableManager.java
521
Popular Tags