001: /**
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package org.apache.openejb.util;
018:
019: import org.apache.openejb.loader.SystemInstance;
020: import org.apache.openejb.loader.FileUtils;
021: import org.apache.log4j.PropertyConfigurator;
022: import org.apache.log4j.SimpleLayout;
023: import org.apache.log4j.ConsoleAppender;
024: import org.apache.log4j.Level;
025:
026: import java.util.ResourceBundle;
027: import java.util.MissingResourceException;
028: import java.util.Properties;
029: import java.util.List;
030: import java.util.ArrayList;
031: import java.util.Map;
032: import java.text.MessageFormat;
033: import java.io.IOException;
034: import java.io.File;
035: import java.io.BufferedInputStream;
036: import java.io.FileInputStream;
037: import java.io.InputStream;
038: import java.io.ByteArrayOutputStream;
039: import java.io.ByteArrayInputStream;
040: import java.io.BufferedOutputStream;
041: import java.io.FileOutputStream;
042: import java.net.URL;
043:
044: public class Log4jLog {
045:
046: protected org.apache.log4j.Logger _logger = null;
047: private LogCategory category;
048: private String baseName;
049:
050: private static final String SUFFIX = ".Messages";
051:
052: private static final String OPENEJB = "org.apache.openejb";
053: /**
054: * Computes the parent of a resource name. E.g. if we pass in a key of
055: * a.b.c, it returns the value a.b
056: */
057: private static final Computable<String, String> heirarchyResolver = new Computable<String, String>() {
058: public String compute(String key) throws InterruptedException {
059: int index = key.lastIndexOf(".");
060: String parent = key.substring(0, index);
061: if (parent.contains(OPENEJB))
062: return parent;
063: return null;
064: }
065: };
066: /**
067: * Simply returns the ResourceBundle for a given baseName
068: */
069: private static final Computable<String, ResourceBundle> bundleResolver = new Computable<String, ResourceBundle>() {
070: public ResourceBundle compute(String baseName)
071: throws InterruptedException {
072: try {
073: return ResourceBundle.getBundle(baseName + SUFFIX);
074: } catch (MissingResourceException e) {
075: return null;
076: }
077: }
078: };
079: /**
080: * Builds a Logger object and returns it
081: */
082: private static final Computable<Object[], Log4jLog> loggerResolver = new Computable<Object[], Log4jLog>() {
083: public Log4jLog compute(Object[] args)
084: throws InterruptedException {
085:
086: Log4jLog logger = new Log4jLog();
087: logger.category = (LogCategory) args[0];
088: logger._logger = org.apache.log4j.Logger
089: .getLogger(logger.category.getName());
090: logger.baseName = (String) args[1];
091: return logger;
092:
093: }
094: };
095: /**
096: * Creates a MessageFormat object for a message and returns it
097: */
098: private static final Computable<String, MessageFormat> messageFormatResolver = new Computable<String, MessageFormat>() {
099: public MessageFormat compute(String message)
100: throws InterruptedException {
101:
102: return new MessageFormat(message);
103:
104: }
105: };
106: /**
107: * Cache of parent-child relationships between resource names
108: */
109: private static final Computable<String, String> heirarchyCache = new Memoizer<String, String>(
110: heirarchyResolver);
111: /**
112: * Cache of ResourceBundles
113: */
114: private static final Computable<String, ResourceBundle> bundleCache = new Memoizer<String, ResourceBundle>(
115: bundleResolver);
116: /**
117: * Cache of Loggers
118: */
119: private static final Computable<Object[], Log4jLog> loggerCache = new Memoizer<Object[], Log4jLog>(
120: loggerResolver);
121: /**
122: * Cache of MessageFormats
123: */
124: private static final Computable<String, MessageFormat> messageFormatCache = new Memoizer<String, MessageFormat>(
125: messageFormatResolver);
126:
127: private static final String LOGGING_PROPERTIES_FILE = "logging.properties";
128: private static final String EMBEDDED_PROPERTIES_FILE = "embedded.logging.properties";
129:
130: static {
131: try {
132: String prop = System.getProperty("openejb.logger.external",
133: "false");
134: boolean externalLogging = Boolean.parseBoolean(prop);
135: if (!externalLogging)
136: configureInternal();
137: } catch (Exception e) {
138: // The fall back here is that if log4j.configuration system property is set, then that configuration file will be used.
139: e.printStackTrace();
140: }
141: }
142:
143: private static void configureInternal() throws IOException {
144:
145: System.setProperty("openjpa.Log", "log4j");
146: SystemInstance system = SystemInstance.get();
147: FileUtils base = system.getBase();
148: File confDir = base.getDirectory("conf");
149: File loggingPropertiesFile = new File(confDir,
150: LOGGING_PROPERTIES_FILE);
151: if (confDir.exists()) {
152: if (loggingPropertiesFile.exists()) {
153: BufferedInputStream bis = new BufferedInputStream(
154: new FileInputStream(loggingPropertiesFile));
155: Properties props = new Properties();
156: props.load(bis);
157: preprocessProperties(props);
158: PropertyConfigurator.configure(props);
159: try {
160: bis.close();
161: } catch (IOException e) {
162:
163: }
164: } else {
165: installLoggingPropertiesFile(loggingPropertiesFile);
166: }
167: } else {
168: configureEmbedded();
169: }
170: }
171:
172: private static void preprocessProperties(Properties properties) {
173: FileUtils base = SystemInstance.get().getBase();
174: File confDir = new File(base.getDirectory(), "conf");
175: File baseDir = base.getDirectory();
176: File userDir = new File("foo").getParentFile();
177:
178: File[] paths = { confDir, baseDir, userDir };
179:
180: List missing = new ArrayList();
181:
182: for (Map.Entry<Object, Object> entry : properties.entrySet()) {
183: String key = (String) entry.getKey();
184: String value = (String) entry.getValue();
185:
186: if (key.endsWith(".File")) {
187:
188: boolean found = false;
189: for (int i = 0; i < paths.length && !found; i++) {
190: File path = paths[i];
191: File logfile = new File(path, value);
192: if (logfile.getParentFile().exists()) {
193: properties.setProperty(key, logfile
194: .getAbsolutePath());
195: found = true;
196: }
197: }
198:
199: if (!found) {
200: File logfile = new File(paths[0], value);
201: missing.add(logfile);
202: }
203: }
204: }
205:
206: if (missing.size() > 0) {
207: org.apache.log4j.Logger logger = getFallabckLogger();
208:
209: logger
210: .error("Logging may not operate as expected. The directories for the following files do not exist so no file can be created. See the list below.");
211: for (int i = 0; i < missing.size(); i++) {
212: File file = (File) missing.get(i);
213: logger.error("[" + i + "] " + file.getAbsolutePath());
214: }
215: }
216: }
217:
218: private static org.apache.log4j.Logger getFallabckLogger() {
219: org.apache.log4j.Logger logger = org.apache.log4j.Logger
220: .getLogger("OpenEJB.logging");
221:
222: SimpleLayout simpleLayout = new SimpleLayout();
223: ConsoleAppender newAppender = new ConsoleAppender(simpleLayout);
224: logger.addAppender(newAppender);
225: return logger;
226: }
227:
228: private static void configureEmbedded() {
229: URL resource = Thread.currentThread().getContextClassLoader()
230: .getResource(EMBEDDED_PROPERTIES_FILE);
231: if (resource != null)
232: PropertyConfigurator.configure(resource);
233: else
234: System.out
235: .println("FATAL ERROR WHILE CONFIGURING LOGGING!!!. MISSING embedded.logging.properties FILE ");
236: }
237:
238: private static void installLoggingPropertiesFile(
239: File loggingPropertiesFile) throws IOException {
240: URL resource = Thread.currentThread().getContextClassLoader()
241: .getResource(LOGGING_PROPERTIES_FILE);
242: if (resource == null) {
243: System.out
244: .println("FATAL ERROR WHILE CONFIGURING LOGGING!!!. MISSING logging.properties FILE ");
245: return;
246: }
247: InputStream in = resource.openStream();
248: in = new BufferedInputStream(in);
249: ByteArrayOutputStream bao = new ByteArrayOutputStream();
250: byte buf[] = new byte[4096];
251: int i = in.read(buf);
252: while (i != -1) {
253: bao.write(buf);
254: i = in.read(buf);
255: }
256: byte[] byteArray = bao.toByteArray();
257: ByteArrayInputStream bis = new ByteArrayInputStream(byteArray);
258:
259: Properties props = new Properties();
260: props.load(bis);
261: preprocessProperties(props);
262: BufferedOutputStream bout = new BufferedOutputStream(
263: new FileOutputStream(loggingPropertiesFile));
264: bout.write(byteArray);
265: PropertyConfigurator.configure(props);
266: try {
267: bout.close();
268: } catch (IOException e) {
269:
270: }
271: try {
272: in.close();
273: } catch (IOException e) {
274:
275: }
276:
277: }
278:
279: /**
280: * Given a key and a baseName, this method computes a message for a key. if
281: * the key is not found in this ResourceBundle for this baseName, then it
282: * recursively looks up its parent to find the message for a key. If no
283: * message is found for a key, the key is returned as is and is logged by
284: * the logger.
285: */
286: private String getMessage(String key, String baseName) {
287: try {
288:
289: ResourceBundle bundle = bundleCache.compute(baseName);
290: if (bundle != null) {
291: String message = null;
292: try {
293: message = bundle.getString(key);
294: return message;
295: } catch (MissingResourceException e) {
296: String parentName = heirarchyCache
297: .compute(baseName);
298: if (parentName == null)
299: return key;
300: else
301: return getMessage(key, parentName);
302: }
303:
304: } else {
305: String parentName = heirarchyCache.compute(baseName);
306: if (parentName == null)
307: return key;
308: else
309: return getMessage(key, parentName);
310:
311: }
312: } catch (InterruptedException e) {
313: // ignore
314: }
315: return key;
316: }
317:
318: /**
319: * Finds a Logger from the cache and returns it. If not found in cache then builds a Logger and returns it.
320: *
321: * @param category - The category of the logger
322: * @param baseName - The baseName for the ResourceBundle
323: * @return Logger
324: */
325: public static Log4jLog getInstance(LogCategory category,
326: String baseName) {
327: try {
328: Log4jLog logger = loggerCache.compute(new Object[] {
329: category, baseName });
330: return logger;
331: } catch (InterruptedException e) {
332: /*
333: * Don't return null here. Just create a new Logger and set it up.
334: * It will not be stored in the cache, but a later lookup for the
335: * same Logger would probably end up in the cache
336: */
337: Log4jLog logger = new Log4jLog();
338: logger.category = category;
339: logger._logger = org.apache.log4j.Logger.getLogger(category
340: .getName());
341: logger.baseName = baseName;
342: return logger;
343: }
344: }
345:
346: public static Log4jLog getInstance(LogCategory category, Class clazz) {
347: return getInstance(category, packageName(clazz));
348: }
349:
350: private static String packageName(Class clazz) {
351: String name = clazz.getName();
352: return name.substring(0, name.lastIndexOf("."));
353: }
354:
355: public Log4jLog getLogger(String moduleId) {
356: return getInstance(this .category, this .baseName);
357:
358: }
359:
360: /**
361: * Formats a given message
362: *
363: * @param message
364: * @param args
365: * @return the formatted message
366: */
367: private String formatMessage(String message, Object... args) {
368: try {
369: MessageFormat mf = messageFormatCache.compute(message);
370: String msg = mf.format(args);
371: return msg;
372: } catch (InterruptedException e) {
373: return "Error in formatting message " + message;
374: }
375:
376: }
377:
378: private Log4jLog() {
379: }
380:
381: public boolean isDebugEnabled() {
382: return _logger.isDebugEnabled();
383: }
384:
385: public boolean isErrorEnabled() {
386: return _logger.isEnabledFor(Level.ERROR);
387: }
388:
389: public boolean isFatalEnabled() {
390: return _logger.isEnabledFor(Level.FATAL);
391: }
392:
393: public boolean isInfoEnabled() {
394: return _logger.isInfoEnabled();
395: }
396:
397: public boolean isWarningEnabled() {
398: return _logger.isEnabledFor(Level.WARN);
399: }
400:
401: /**
402: * If this level is enabled, then it finds a message for the given key and logs it
403: *
404: * @param message - This could be a plain message or a key in Messages.properties
405: * @return the formatted i18n message
406: */
407: public String debug(String message) {
408:
409: if (isDebugEnabled()) {
410: String msg = getMessage(message, baseName);
411: _logger.debug(msg);
412: return msg;
413: }
414: return message;
415: }
416:
417: public String debug(String message, Object... args) {
418:
419: if (isDebugEnabled()) {
420: String msg = getMessage(message, baseName);
421: msg = formatMessage(msg, args);
422: _logger.debug(msg);
423: return msg;
424: }
425: return message;
426: }
427:
428: public String debug(String message, Throwable t) {
429:
430: if (isDebugEnabled()) {
431: String msg = getMessage(message, baseName);
432: _logger.debug(msg, t);
433: return msg;
434: }
435: return message;
436: }
437:
438: public String debug(String message, Throwable t, Object... args) {
439:
440: if (isDebugEnabled()) {
441: String msg = getMessage(message, baseName);
442: msg = formatMessage(msg, args);
443: _logger.debug(msg, t);
444: return msg;
445: }
446: return message;
447: }
448:
449: public String error(String message) {
450:
451: if (isErrorEnabled()) {
452: String msg = getMessage(message, baseName);
453: _logger.error(msg);
454: return msg;
455: }
456: return message;
457: }
458:
459: public String error(String message, Object... args) {
460:
461: if (isErrorEnabled()) {
462: String msg = getMessage(message, baseName);
463: msg = formatMessage(msg, args);
464: _logger.error(msg);
465: return msg;
466: }
467: return message;
468: }
469:
470: public String error(String message, Throwable t) {
471:
472: if (isErrorEnabled()) {
473: String msg = getMessage(message, baseName);
474: _logger.error(msg, t);
475: return msg;
476: }
477: return message;
478: }
479:
480: public String error(String message, Throwable t, Object... args) {
481:
482: if (isErrorEnabled()) {
483: String msg = getMessage(message, baseName);
484: msg = formatMessage(msg, args);
485: _logger.error(msg, t);
486: return msg;
487: }
488: return message;
489: }
490:
491: public String fatal(String message) {
492: if (isFatalEnabled()) {
493: String msg = getMessage(message, baseName);
494: _logger.fatal(msg);
495: return msg;
496: }
497: return message;
498: }
499:
500: public String fatal(String message, Object... args) {
501: if (isFatalEnabled()) {
502: String msg = getMessage(message, baseName);
503: msg = formatMessage(msg, args);
504: _logger.fatal(msg);
505: return msg;
506: }
507: return message;
508: }
509:
510: public String fatal(String message, Throwable t) {
511: if (isFatalEnabled()) {
512: String msg = getMessage(message, baseName);
513: _logger.fatal(msg, t);
514: return msg;
515: }
516: return message;
517: }
518:
519: public String fatal(String message, Throwable t, Object... args) {
520: if (isFatalEnabled()) {
521: String msg = getMessage(message, baseName);
522: msg = formatMessage(msg, args);
523: _logger.fatal(msg, t);
524: return msg;
525: }
526: return message;
527: }
528:
529: public String info(String message) {
530: if (isInfoEnabled()) {
531: String msg = getMessage(message, baseName);
532: _logger.info(msg);
533: return msg;
534: }
535: return message;
536: }
537:
538: public String info(String message, Object... args) {
539: if (isInfoEnabled()) {
540: String msg = getMessage(message, baseName);
541: msg = formatMessage(msg, args);
542: _logger.info(msg);
543: return msg;
544: }
545: return message;
546: }
547:
548: public String info(String message, Throwable t) {
549: if (isInfoEnabled()) {
550: String msg = getMessage(message, baseName);
551: _logger.info(msg, t);
552: return msg;
553: }
554: return message;
555: }
556:
557: public String info(String message, Throwable t, Object... args) {
558: if (isInfoEnabled()) {
559: String msg = getMessage(message, baseName);
560: msg = formatMessage(msg, args);
561: _logger.info(msg, t);
562: return msg;
563: }
564: return message;
565: }
566:
567: public String warning(String message) {
568: if (isWarningEnabled()) {
569: String msg = getMessage(message, baseName);
570: _logger.warn(msg);
571: return msg;
572: }
573: return message;
574: }
575:
576: public String warning(String message, Object... args) {
577: if (isWarningEnabled()) {
578: String msg = getMessage(message, baseName);
579: msg = formatMessage(msg, args);
580: _logger.warn(msg);
581: return msg;
582: }
583: return message;
584: }
585:
586: public String warning(String message, Throwable t) {
587: if (isWarningEnabled()) {
588: String msg = getMessage(message, baseName);
589: _logger.warn(msg, t);
590: return msg;
591: }
592: return message;
593: }
594:
595: public String warning(String message, Throwable t, Object... args) {
596: if (isWarningEnabled()) {
597: String msg = getMessage(message, baseName);
598: msg = formatMessage(msg, args);
599: _logger.warn(msg, t);
600: return msg;
601: }
602: return message;
603: }
604:
605: }
|