KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > RolapUtil


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/rolap/RolapUtil.java#49 $
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) 2001-2002 Kana Software, Inc.
7 // Copyright (C) 2001-2007 Julian Hyde and others
8 // All Rights Reserved.
9 // You must accept the terms of that agreement to use this software.
10 //
11 // jhyde, 22 December, 2001
12 */

13
14 package mondrian.rolap;
15 import mondrian.olap.*;
16 import mondrian.olap.fun.FunUtil;
17 import mondrian.resource.MondrianResource;
18
19 import org.apache.log4j.Logger;
20 import org.eigenbase.util.property.StringProperty;
21 import java.io.*;
22 import java.lang.reflect.Array JavaDoc;
23 import java.sql.SQLException JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.StringTokenizer JavaDoc;
28
29 import mondrian.calc.ExpCompiler;
30
31 import javax.sql.DataSource JavaDoc;
32
33 /**
34  * Utility methods for classes in the <code>mondrian.rolap</code> package.
35  *
36  * @author jhyde
37  * @since 22 December, 2001
38  * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapUtil.java#49 $
39  */

40 public class RolapUtil {
41
42     static final Logger LOGGER = Logger.getLogger(RolapUtil.class);
43     static final RolapMember[] emptyMemberArray = new RolapMember[0];
44     private static PrintWriter traceOut = null;
45     private static boolean checkedTracing = false;
46     private static boolean traceEnabled;
47     private static Semaphore querySemaphore;
48
49     /**
50      * Special cell value indicates that the value is not in cache yet.
51      */

52     public static final Object JavaDoc valueNotReadyException = new Double JavaDoc(0);
53
54     /**
55      * Hook to run when a query is executed.
56      */

57     static final ThreadLocal JavaDoc<ExecuteQueryHook> threadHooks =
58         new ThreadLocal JavaDoc<ExecuteQueryHook>();
59
60     /**
61      * Special value represents a null key.
62      */

63     public static final Comparable JavaDoc sqlNullValue = new Comparable JavaDoc() {
64         public boolean equals(Object JavaDoc o) {
65             return o == this;
66         }
67         public int hashCode() {
68             return super.hashCode();
69         }
70         public String JavaDoc toString() {
71             return "#null";
72         }
73
74         public int compareTo(Object JavaDoc o) {
75             return o == this ? 0 : -1;
76         }
77     };
78
79     public static final String JavaDoc mdxNullLiteral = "#null";
80     public static final String JavaDoc sqlNullLiteral = "null";
81
82     /**
83      * Names of classes of drivers we've loaded (or have tried to load).
84      * @synchronization Lock the {@link RolapConnection} class.
85      */

86     private static final HashSet JavaDoc<String JavaDoc> loadedDrivers = new HashSet JavaDoc<String JavaDoc>();
87
88     static RolapMember[] toArray(List JavaDoc<RolapMember> v) {
89         return v.isEmpty()
90             ? emptyMemberArray
91             : v.toArray(new RolapMember[v.size()]);
92     }
93
94     static RolapMember lookupMember(
95             MemberReader reader,
96             String JavaDoc[] uniqueNameParts,
97             boolean failIfNotFound) {
98         RolapMember member =
99             lookupMemberInternal(
100                 uniqueNameParts, null, reader, failIfNotFound);
101         if (member != null) {
102             return member;
103         }
104
105         // If this hierarchy has an 'all' member, we can omit it.
106
// For example, '[Gender].[(All Gender)].[F]' can be abbreviated
107
// '[Gender].[F]'.
108
final List JavaDoc<RolapMember> rootMembers = reader.getRootMembers();
109         if (rootMembers.size() == 1) {
110             final RolapMember rootMember = rootMembers.get(0);
111             if (rootMember.isAll()) {
112                 member =
113                     lookupMemberInternal(
114                         uniqueNameParts, rootMember, reader, failIfNotFound);
115             }
116         }
117         return member;
118     }
119
120     private static RolapMember lookupMemberInternal(
121         String JavaDoc[] uniqueNameParts,
122         RolapMember member,
123         MemberReader reader,
124         boolean failIfNotFound)
125     {
126         for (String JavaDoc name : uniqueNameParts) {
127             List JavaDoc<RolapMember> children;
128             if (member == null) {
129                 children = reader.getRootMembers();
130             } else {
131                 children = new ArrayList JavaDoc<RolapMember>();
132                 reader.getMemberChildren(member, children);
133                 member = null;
134             }
135             for (RolapMember child : children) {
136                 if (child.getName().equals(name)) {
137                     member = child;
138                     break;
139                 }
140             }
141             if (member == null) {
142                 break;
143             }
144         }
145         if (member == null && failIfNotFound) {
146             throw MondrianResource.instance().MdxCantFindMember.ex(
147                 Util.implode(uniqueNameParts));
148         }
149         return member;
150     }
151
152     /**
153      * Adds an object to the end of an array. The resulting array is of the
154      * same type (e.g. <code>String[]</code>) as the input array.
155      */

156     static <T> T[] addElement(T[] a, T o) {
157         Class JavaDoc clazz = a.getClass().getComponentType();
158         T[] a2 = (T[]) Array.newInstance(clazz, a.length + 1);
159         System.arraycopy(a, 0, a2, 0, a.length);
160         a2[a.length] = o;
161         return a2;
162     }
163
164     /**
165      * Adds an array to the end of an array. The resulting array is of the
166      * same type (e.g. <code>String[]</code>) as the input array.
167      */

168     static <T> T[] addElements(T[] a, T[] b) {
169         Class JavaDoc clazz = a.getClass().getComponentType();
170         T[] c = (T[]) Array.newInstance(clazz, a.length + b.length);
171         System.arraycopy(a, 0, c, 0, a.length);
172         System.arraycopy(b, 0, c, a.length, b.length);
173         return c;
174     }
175
176     /**
177      * Checks whether tracing is enabled, and returns a PrintWriter if so.
178      *
179      * <p>Tracing is enabled if the {@link MondrianProperties#TraceLevel}
180      * property is greater than 0 and a debug output file
181      * ({@link MondrianProperties#DebugOutFile}) is configured.
182      *
183      * <p>These properties are only checked the first time this method is
184      * called; subsequent invocations return the cached result.
185      *
186      * @return A PrintWriter if tracing is enabled, otherwise null
187      */

188     public static synchronized PrintWriter checkTracing() {
189         if (!checkedTracing) {
190             int trace = MondrianProperties.instance().TraceLevel.get();
191             if (trace > 0) {
192                 String JavaDoc debugOutFile =
193                         MondrianProperties.instance().DebugOutFile.get();
194                 if (debugOutFile != null) {
195                     File f;
196                     try {
197                         f = new File(debugOutFile);
198                         setDebugOut(new PrintWriter(new FileOutputStream(f), true));
199                     } catch (Exception JavaDoc e) {
200                         setDebugOut(new PrintWriter(System.out, true));
201                     }
202                 } else {
203                     setDebugOut(new PrintWriter(System.out, true));
204                 }
205                 traceEnabled = true;
206             } else {
207                 traceEnabled = false;
208             }
209             checkedTracing = true;
210         }
211         return traceEnabled ? traceOut : null;
212     }
213
214     /**
215      * Redirects debug output to another PrintWriter.
216      * @param pw A PrintWriter
217      */

218     static public void setDebugOut(PrintWriter pw) {
219         traceOut = pw;
220     }
221
222     /**
223      * Executes a query, printing to the trace log if tracing is enabled.
224      *
225      * <p>If the query fails, it wraps the {@link SQLException} in a runtime
226      * exception with <code>message</code> as description, and closes the result
227      * set.
228      *
229      * <p>If it succeeds, the caller must call the {@link SqlStatement#close}
230      * method of the returned {@link SqlStatement}.
231      *
232      * @param dataSource DataSource
233      * @param sql SQL string
234      * @param component Description of a the component executing the query,
235      * generally a method name, e.g. "SqlTupleReader.readTuples"
236      * @param message Description of the purpose of this statement, to be
237      * printed if there is an error
238      * @return ResultSet
239      */

240     public static SqlStatement executeQuery(
241         DataSource JavaDoc dataSource,
242         String JavaDoc sql,
243         String JavaDoc component,
244         String JavaDoc message)
245     {
246         return executeQuery(
247             dataSource, sql, -1, component, message, -1, -1);
248     }
249
250     /**
251      * Executes a query.
252      *
253      * <p>If the query fails, it wraps the {@link SQLException} in a runtime
254      * exception with <code>message</code> as description, and closes the result
255      * set.
256      *
257      * <p>If it succeeds, the caller must call the {@link SqlStatement#close}
258      * method of the returned {@link SqlStatement}.
259      *
260      * @param dataSource DataSource
261      * @param sql SQL string
262      * @param maxRows Row limit, or -1 if no limit
263      * @param component Description of a the component executing the query,
264      * generally a method name, e.g. "SqlTupleReader.readTuples"
265      * @param message Description of the purpose of this statement, to be
266      * printed if there is an error
267      * @param resultSetType Result set type, or -1 to use default
268      * @param resultSetConcurrency Result set concurrency, or -1 to use default
269      * @return ResultSet
270      */

271     public static SqlStatement executeQuery(
272         DataSource JavaDoc dataSource,
273         String JavaDoc sql,
274         int maxRows,
275         String JavaDoc component,
276         String JavaDoc message,
277         int resultSetType,
278         int resultSetConcurrency)
279     {
280         SqlStatement stmt =
281             new SqlStatement(
282                 dataSource, sql, maxRows, component, message,
283                 resultSetType, resultSetConcurrency);
284         try {
285             stmt.execute();
286             return stmt;
287         } catch (SQLException JavaDoc e) {
288             throw stmt.handle(e);
289         }
290     }
291
292     /**
293      * Raises an alert that native SQL evaluation could not be used
294      * in a case where it might have been beneficial, but some
295      * limitation in Mondrian's implementation prevented it.
296      * (Do not call this in cases where native evaluation would
297      * have been wasted effort.)
298      *
299      * @param functionName name of function for which native evaluation
300      * was skipped
301      *
302      * @param reason reason why native evaluation was skipped
303      */

304     public static void alertNonNative(
305         String JavaDoc functionName, String JavaDoc reason)
306         throws NativeEvaluationUnsupportedException {
307
308         // No i18n for log message, but yes for excn
309
String JavaDoc alertMsg =
310             "Unable to use native SQL evaluation for '" + functionName
311             + "'; reason: " + reason;
312
313         StringProperty alertProperty =
314             MondrianProperties.instance().AlertNativeEvaluationUnsupported;
315         String JavaDoc alertValue = alertProperty.get();
316
317         if (alertValue.equalsIgnoreCase(
318                 org.apache.log4j.Level.WARN.toString())) {
319             LOGGER.warn(alertMsg);
320         } else if (alertValue.equalsIgnoreCase(
321                        org.apache.log4j.Level.ERROR.toString())) {
322             LOGGER.error(alertMsg);
323             throw MondrianResource.instance().NativeEvaluationUnsupported.ex(
324                 functionName);
325         }
326     }
327
328     /**
329      * Loads a set of JDBC drivers.
330      *
331      * @param jdbcDrivers A string consisting of the comma-separated names
332      * of JDBC driver classes. For example
333      * <code>"sun.jdbc.odbc.JdbcOdbcDriver,com.mysql.jdbc.Driver"</code>.
334      */

335     public static synchronized void loadDrivers(String JavaDoc jdbcDrivers) {
336         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(jdbcDrivers, ",");
337         while (tok.hasMoreTokens()) {
338             String JavaDoc jdbcDriver = tok.nextToken();
339             if (loadedDrivers.add(jdbcDriver)) {
340                 try {
341                     Class.forName(jdbcDriver);
342                     LOGGER.info("Mondrian: JDBC driver "
343                         + jdbcDriver + " loaded successfully");
344                 } catch (ClassNotFoundException JavaDoc e) {
345                     LOGGER.warn("Mondrian: Warning: JDBC driver "
346                         + jdbcDriver + " not found");
347                 }
348             }
349         }
350     }
351
352     /**
353      * Creates a compiler which will generate programs which will test
354      * whether the dependencies declared via
355      * {@link mondrian.calc.Calc#dependsOn(mondrian.olap.Dimension)} are
356      * accurate.
357      */

358     public static ExpCompiler createDependencyTestingCompiler(
359             ExpCompiler compiler) {
360         return new RolapDependencyTestingEvaluator.DteCompiler(compiler);
361     }
362
363     /**
364      * Locates a member specified by its member name, from an array of
365      * members. If an exact match isn't found, but a matchType of BEFORE
366      * or AFTER is specified, then the closest matching member is returned.
367      *
368      * @param members array of members to search from
369      * @param parent parent member corresponding to the member being searched
370      * for
371      * @param level level of the member
372      * @param searchName member name
373      * @param matchType match type
374      * @param caseInsensitive if true, use case insensitive search (if
375      * applicable) when when doing exact searches
376      *
377      * @return matching member (if it exists) or the closest matching one
378      * in the case of a BEFORE or AFTER search
379      */

380     public static Member findBestMemberMatch(
381         List JavaDoc<? extends Member> members,
382         RolapMember parent,
383         RolapLevel level,
384         String JavaDoc searchName,
385         MatchType matchType,
386         boolean caseInsensitive)
387     {
388         // create a member corresponding to the member we're trying
389
// to locate so we can use it to hierarchically compare against
390
// the members array
391
RolapMember searchMember = new RolapMember(parent, level, searchName);
392         Member bestMatch = null;
393         for (Member member : members) {
394             int rc;
395             if (matchType == MatchType.EXACT) {
396                 if (caseInsensitive) {
397                     rc = Util.compareName(member.getName(), searchName);
398                 } else {
399                     rc = member.getName().compareTo(searchName);
400                 }
401             } else {
402                 rc =
403                     FunUtil.compareSiblingMembers(
404                         member,
405                         searchMember);
406             }
407             if (rc == 0) {
408                 return member;
409             }
410             if (matchType == MatchType.BEFORE) {
411                 if (rc < 0 &&
412                     (bestMatch == null ||
413                         FunUtil.compareSiblingMembers(member, bestMatch) > 0)) {
414                     bestMatch = member;
415                 }
416             } else if (matchType == MatchType.AFTER) {
417                 if (rc > 0 &&
418                     (bestMatch == null ||
419                         FunUtil.compareSiblingMembers(member, bestMatch) < 0)) {
420                     bestMatch = member;
421                 }
422             }
423         }
424         if (matchType == MatchType.EXACT) {
425             return null;
426         }
427         return bestMatch;
428     }
429
430     /**
431      * Writes to a string and also to an underlying writer.
432      */

433     public static class TeeWriter extends FilterWriter {
434         StringWriter buf = new StringWriter();
435         public TeeWriter(Writer out) {
436             super(out);
437         }
438
439         /**
440          * Returns everything which has been written so far.
441          */

442         public String JavaDoc toString() {
443             return buf.toString();
444         }
445
446         /**
447          * Returns the underlying writer.
448          */

449         public Writer getWriter() {
450             return out;
451         }
452
453         public void write(int c) throws IOException {
454             super.write(c);
455             buf.write(c);
456         }
457
458         public void write(char cbuf[]) throws IOException {
459             super.write(cbuf);
460             buf.write(cbuf);
461         }
462
463         public void write(char cbuf[], int off, int len) throws IOException {
464             super.write(cbuf, off, len);
465             buf.write(cbuf, off, len);
466         }
467
468         public void write(String JavaDoc str) throws IOException {
469             super.write(str);
470             buf.write(str);
471         }
472
473         public void write(String JavaDoc str, int off, int len) throws IOException {
474             super.write(str, off, len);
475             buf.write(str, off, len);
476         }
477     }
478
479     /**
480      * Writer which throws away all input.
481      */

482     private static class NullWriter extends Writer {
483         public void write(char cbuf[], int off, int len) throws IOException {
484         }
485
486         public void flush() throws IOException {
487         }
488
489         public void close() throws IOException {
490         }
491     }
492
493     /**
494      * Creates a {@link TeeWriter} which captures everything which goes through
495      * {@link #traceOut} from now on.
496      */

497     public static synchronized TeeWriter startTracing() {
498         TeeWriter tw;
499         if (traceOut == null) {
500             tw = new TeeWriter(new NullWriter());
501         } else {
502             tw = new TeeWriter(RolapUtil.traceOut);
503         }
504         traceOut = new PrintWriter(tw);
505         return tw;
506     }
507
508     /**
509      * Gets the semaphore which controls how many people can run queries
510      * simultaneously.
511      */

512     static synchronized Semaphore getQuerySemaphore() {
513         if (querySemaphore == null) {
514             int queryCount = MondrianProperties.instance().QueryLimit.get();
515             querySemaphore = new Semaphore(queryCount);
516         }
517         return querySemaphore;
518     }
519
520     /**
521      * Creates a dummy evaluator.
522      */

523     public static Evaluator createEvaluator(Query query) {
524         final RolapResult result = new RolapResult(query, false);
525         return result.getRootEvaluator();
526     }
527
528     /**
529      * A <code>Semaphore</code> is a primitive for process synchronization.
530      *
531      * <p>Given a semaphore initialized with <code>count</code>, no more than
532      * <code>count</code> threads can acquire the semaphore using the
533      * {@link #enter} method. Waiting threads block until enough threads have
534      * called {@link #leave}.
535      */

536     static class Semaphore {
537         private int count;
538         Semaphore(int count) {
539             if (count < 0) {
540                 count = Integer.MAX_VALUE;
541             }
542             this.count = count;
543         }
544         synchronized void enter() {
545             if (count == Integer.MAX_VALUE) {
546                 return;
547             }
548             if (count == 0) {
549                 try {
550                     wait();
551                 } catch (InterruptedException JavaDoc e) {
552                     throw Util.newInternal(e, "while waiting for semaphore");
553                 }
554             }
555             Util.assertTrue(count > 0);
556             count--;
557         }
558         synchronized void leave() {
559             if (count == Integer.MAX_VALUE) {
560                 return;
561             }
562             count++;
563             notify();
564         }
565     }
566
567     static interface ExecuteQueryHook {
568         void onExecuteQuery(String JavaDoc sql);
569     }
570
571 }
572
573 // End RolapUtil.java
574
Popular Tags