001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.internal.ejb.cmp3.metadata;
038:
039: import java.io.InputStream;
040: import java.io.IOException;
041:
042: import java.net.URISyntaxException;
043: import java.net.URL;
044:
045: import java.util.Collection;
046: import java.util.Enumeration;
047: import java.util.HashMap;
048: import java.util.HashSet;
049: import java.util.Map;
050: import java.util.Set;
051:
052: import javax.persistence.spi.PersistenceUnitInfo;
053:
054: import oracle.toplink.essentials.ejb.cmp3.persistence.Archive;
055: import oracle.toplink.essentials.ejb.cmp3.persistence.ArchiveFactoryImpl;
056: import oracle.toplink.essentials.ejb.cmp3.persistence.PersistenceUnitProcessor;
057:
058: import oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException;
059: import oracle.toplink.essentials.exceptions.ValidationException;
060:
061: import oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerSetupImpl;
062: import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataLogger;
063: import oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.ClassAccessor;
064: import oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.objects.MetadataClass;
065:
066: import oracle.toplink.essentials.internal.ejb.cmp3.xml.accessors.XMLClassAccessor;
067: import oracle.toplink.essentials.internal.ejb.cmp3.xml.XMLConstants;
068: import oracle.toplink.essentials.internal.ejb.cmp3.xml.XMLHelper;
069: import oracle.toplink.essentials.internal.ejb.cmp3.xml.XMLValidator;
070:
071: import oracle.toplink.essentials.internal.sessions.AbstractSession;
072:
073: import oracle.toplink.essentials.logging.AbstractSessionLog;
074: import oracle.toplink.essentials.logging.SessionLog;
075:
076: import org.w3c.dom.Document;
077: import org.w3c.dom.Node;
078: import org.w3c.dom.NodeList;
079:
080: /**
081: * The object/relational metadata processor for the EJB3.0 specification.
082: *
083: * @author Guy Pelletier, Sanjeeb.Sahoo@Sun.COM
084: * @since TopLink EJB 3.0 Reference Implementation
085: */
086: public class MetadataProcessor {
087: /*
088: * Design Pattern in use: Builder pattern
089: * EntityManagerSetupImpl, MetadataProcessor and MetadataProject
090: * play the role of director, builder and product respectively.
091: */
092: protected ClassLoader m_loader;
093: protected MetadataLogger m_logger;
094: protected MetadataProject m_project;
095: protected MetadataValidator m_validator;
096: protected AbstractSession m_session;
097:
098: /**
099: * INTERNAL:
100: * Called from EntityManagerSetupImpl. The 'real' EJB 3.0 processing
101: * that includes XML and annotations.
102: */
103: public MetadataProcessor(PersistenceUnitInfo puInfo,
104: AbstractSession session, ClassLoader loader,
105: boolean enableLazyForOneToOne) {
106: m_loader = loader;
107: m_session = session;
108: m_logger = new MetadataLogger(session);
109: m_project = new MetadataProject(puInfo, session,
110: enableLazyForOneToOne);
111: }
112:
113: /**
114: * INTERNAL:
115: * Called from RelationshipWeaverTestSuite. Use this constructor to avoid
116: * XML processing.
117: * @deprecated
118: */
119: public MetadataProcessor(AbstractSession session,
120: ClassLoader loader, Collection<Class> entities,
121: boolean enableLazyForOneToOne) {
122: m_loader = loader;
123: m_project = new MetadataProject(null, session,
124: enableLazyForOneToOne);
125: m_session = session;
126: Collection<String> entityNames = new HashSet<String>(entities
127: .size());
128: for (Class entity : entities) {
129: m_project.addDescriptor(new MetadataDescriptor(entity));
130: entityNames.add(entity.getName());
131: }
132: m_project.setEntityNames(entityNames);
133: m_logger = new MetadataLogger(session);
134: }
135:
136: /**
137: * INTERNAL:
138: * Method to place EntityListener's on the descriptors from the given
139: * session. This call is made from the EntityManagerSetup deploy call.
140: */
141: public void addEntityListeners() {
142: for (MetadataDescriptor descriptor : m_project.getDescriptors()) {
143: // Process all descriptors that are in our project.
144: ClassAccessor accessor = descriptor.getClassAccessor();
145:
146: descriptor.setJavaClass(descriptor.getClassDescriptor()
147: .getJavaClass());
148: // The class loader has changed, update the class stored for
149: // our class accessor and its list of mapped superclasses.
150: accessor.setAnnotatedElement(descriptor.getJavaClass());
151: accessor.clearMappedSuperclasses();
152:
153: accessor.processListeners(m_loader);
154: }
155: }
156:
157: /**
158: * INTERNAL:
159: * Method to place NamedQueries and NamedNativeQueries on the given session.
160: * This call is made from the EntityManagerSetup deploy call.
161: */
162: public void addNamedQueries() {
163: m_project.processNamedQueries(m_validator);
164: m_project.processNamedNativeQueries(m_loader);
165: }
166:
167: /**
168: * INTERNAL:
169: * Return a set of class names for each entity found in the xml
170: * descriptor instance document.
171: */
172: private static Set<String> buildEntityClassSetFromXMLDocument(
173: Document document, String fileName, ClassLoader loader) {
174: XMLHelper helper = new XMLHelper(document, fileName, loader);
175:
176: // Process the package node.
177: String defaultPkg = helper.getNodeValue(new String[] {
178: XMLConstants.ENTITY_MAPPINGS, XMLConstants.PACKAGE,
179: XMLConstants.TEXT });
180:
181: // Handle entities only. Mapped superclasses and embeddables are
182: // discovered and processed separately.
183: HashSet<String> classSet = new HashSet<String>();
184: classSet.addAll(buildEntityClassSetForNodeList(helper,
185: XMLConstants.ENTITY, defaultPkg));
186:
187: return classSet;
188: }
189:
190: /**
191: * INTERNAL:
192: * The class name of each node in the node list will be added to the
193: * provided collection.
194: */
195: private static Set<String> buildEntityClassSetForNodeList(
196: XMLHelper helper, String xPath, String defaultPkg) {
197: HashSet<String> classNames = new HashSet<String>();
198: NodeList nodes = helper.getNodes(XMLConstants.ENTITY_MAPPINGS,
199: xPath);
200: int nodeCount = nodes.getLength();
201:
202: for (int i = 0; i < nodeCount; i++) {
203: // Process the required class attribute node.
204: classNames.add(XMLHelper.getFullyQualifiedClassName(helper
205: .getNode(nodes.item(i), XMLConstants.ATT_CLASS)
206: .getNodeValue(), defaultPkg));
207: }
208:
209: return classNames;
210: }
211:
212: /**
213: * INTERNAL:
214: * Return the logger used by the processor.
215: */
216: public MetadataLogger getLogger() {
217: return m_logger;
218: }
219:
220: /**
221: * INTERNAL:
222: */
223: public MetadataProject getProject() {
224: return m_project;
225: }
226:
227: /**
228: * INTERNAL:
229: * Return the validator used by the processor.
230: */
231: public MetadataValidator getValidator() {
232: return m_validator;
233: }
234:
235: /**
236: * INTERNAL:
237: * Called from RelationshipWeaverTestSuite which uses only annotations
238: * and no XML.
239: */
240: public void processAnnotations() {
241: // Set the correct contextual validator.
242: m_validator = new MetadataValidator();
243:
244: // take a copy of the collection to avoid concurrent modification exception
245: // that would result when embeddables are added lazily.
246: for (MetadataDescriptor descriptor : m_project.getDescriptors()
247: .toArray(new MetadataDescriptor[] {})) {
248: // Process all descriptors that are in our project.
249: ClassAccessor accessor = descriptor.getClassAccessor();
250:
251: // If there is no accessor on this descriptor then it has not been
252: // processed yet. Create one and process it.
253: if (accessor == null) {
254: accessor = new ClassAccessor(new MetadataClass(
255: descriptor.getJavaClass()), this , descriptor);
256: descriptor.setClassAccessor(accessor);
257: accessor.process();
258: }
259: }
260:
261: // Process the project and anything that was deferred like
262: // sequencing and relationship mappings and we are done.
263: m_project.process();
264: }
265:
266: /**
267: * INTERNAL:
268: * Process persistence unit metadata and defaults, and apply them to each
269: * entity in the collection. Any conflicts in elements defined in multiple
270: * documents will cause an exception to be thrown. The first instance
271: * encountered wins, i.e. any conflicts between PU metadata definitions in
272: * multiple instance documents will cause an exception to be thrown. The
273: * one exception to this rule is default listeners: all default listeners
274: * found will be added to a list in the order that they are read from the
275: * instance document(s).
276: */
277: public void processPersistenceUnitMetadata() {
278: // For each orm xml instance document, process persistence unit
279: // metadata/defaults and mapped superclasses.
280: for (Map.Entry<URL, Document> mfDocPair : m_project
281: .getMappingFiles().entrySet()) {
282: // Initialize a helper for navigating the instance document.
283: XMLHelper helper = new XMLHelper(mfDocPair.getValue(),
284: mfDocPair.getKey().getFile(), m_loader);
285:
286: // Store all mapped-superclasses.
287: NodeList nodes = helper.getNodes(
288: XMLConstants.ENTITY_MAPPINGS,
289: XMLConstants.MAPPED_SUPERCLASS);
290:
291: for (int i = 0; i < nodes.getLength(); i++) {
292: Node node = nodes.item(i);
293: Class cls = helper.getNodeValue(nodes.item(i),
294: XMLConstants.ATT_CLASS, void.class);
295: m_project.addMappedSuperclass(cls, node, helper);
296: }
297:
298: // Store all embeddable classes.
299: nodes = helper.getNodes(XMLConstants.ENTITY_MAPPINGS,
300: XMLConstants.EMBEDDABLE);
301:
302: for (int i = 0; i < nodes.getLength(); i++) {
303: Node node = nodes.item(i);
304: Class cls = helper.getNodeValue(nodes.item(i),
305: XMLConstants.ATT_CLASS, void.class);
306: m_project.addEmbeddable(cls, node, helper);
307: }
308:
309: // Look for a persistence-unit-metadata node.
310: Node persistenceUnitMetadataNode = helper
311: .getNode(new String[] {
312: XMLConstants.ENTITY_MAPPINGS,
313: XMLConstants.PU_METADATA });
314:
315: if (persistenceUnitMetadataNode != null) {
316: MetadataPersistenceUnit persistenceUnit = new MetadataPersistenceUnit();
317:
318: // Process the xml-mapping-metadata-complete tag.
319: persistenceUnit.setIsMetadataComplete(helper.getNode(
320: persistenceUnitMetadataNode,
321: XMLConstants.METADATA_COMPLETE) != null);
322:
323: // process persistence unit defaults
324: Node persistenceUnitDefaultsNode = helper.getNode(
325: persistenceUnitMetadataNode,
326: XMLConstants.PU_DEFAULTS);
327:
328: if (persistenceUnitDefaultsNode != null) {
329: // Process the persistence unit access.
330: persistenceUnit.setAccess(helper.getNodeTextValue(
331: persistenceUnitDefaultsNode,
332: XMLConstants.ACCESS));
333:
334: // Process the persitence unit schema.
335: persistenceUnit.setSchema(helper.getNodeTextValue(
336: persistenceUnitDefaultsNode,
337: XMLConstants.SCHEMA));
338:
339: // Process the persistence unit catalog.
340: persistenceUnit.setCatalog(helper.getNodeTextValue(
341: persistenceUnitDefaultsNode,
342: XMLConstants.CATALOG));
343:
344: // Process the persistence unit cascade-persist.
345: persistenceUnit.setIsCascadePersist(helper.getNode(
346: persistenceUnitDefaultsNode,
347: XMLConstants.CASCADE_PERSIST) != null);
348:
349: // Process the default entity-listeners. No conflict
350: // checking will be done, that is, any and all
351: // default listeners will be added to the project.
352: NodeList listenerNodes = helper.getNodes(
353: persistenceUnitDefaultsNode,
354: XMLConstants.ENTITY_LISTENERS,
355: XMLConstants.ENTITY_LISTENER);
356: if (listenerNodes != null) {
357: m_project.addDefaultListeners(listenerNodes,
358: helper);
359: }
360: }
361:
362: // Add the metadata persistence unit to the project if
363: // there is no conflicting metadata (from other
364: // persistence unit metadata)
365: MetadataPersistenceUnit existingPersistenceUnit = m_project
366: .getPersistenceUnit();
367: if (existingPersistenceUnit != null) {
368: if (!existingPersistenceUnit
369: .equals(persistenceUnit)) {
370: (new XMLValidator())
371: .throwPersistenceUnitMetadataConflict(existingPersistenceUnit
372: .getConflict());
373: }
374: } else {
375: m_project.setPersistenceUnit(persistenceUnit);
376: }
377: }
378: }
379: }
380:
381: /**
382: * INTERNAL:
383: * Use this method to set the correct class loader that should be used
384: * during processing.
385: */
386: public void setClassLoader(ClassLoader loader) {
387: m_loader = loader;
388: }
389:
390: /**
391: * This method is responsible for figuring out list of mapping files
392: * to be read for a persistence unit and storing that list in
393: * {@link MetadataProject}.
394: * @param throwExceptionOnFail
395: */
396: public void readMappingFiles(boolean throwExceptionOnFail) {
397: // Initialize the correct contextual objects.
398: m_validator = new XMLValidator();
399:
400: // step #1: discover all the standard XML mapping files.
401: Map<URL, Document> list = readStandardMappingFiles();
402:
403: // step #2: add URLs corresponding to explicitly specified files
404: list
405: .putAll(readExplicitlySpecifiedMappingFiles(throwExceptionOnFail));
406: m_project.setMappingFiles(list);
407: }
408:
409: private Map<URL, Document> readStandardMappingFiles() {
410: Map<URL, Document> list = new HashMap<URL, Document>();
411: final PersistenceUnitInfo puInfo = m_project.getPUInfo();
412: Collection<URL> rootUrls = new HashSet<URL>(puInfo
413: .getJarFileUrls());
414: rootUrls.add(puInfo.getPersistenceUnitRootUrl());
415: final String ormXMLFile = "META-INF/orm.xml";
416: for (URL rootURL : rootUrls) {
417: logMessage("Searching for default mapping file in "
418: + rootURL); // NOI18N
419: URL ormURL = null;
420: InputStream stream = null;
421: try {
422: Archive m_par = null;
423: m_par = new ArchiveFactoryImpl().createArchive(rootURL);
424: ormURL = m_par.getEntryAsURL(ormXMLFile);
425: stream = m_par.getEntry(ormXMLFile);
426: } catch (IOException e) {
427: throw new RuntimeException(e);
428: } catch (URISyntaxException e) {
429: throw new RuntimeException(e);
430: }
431: if (stream != null) {
432: logMessage("Found a default mapping file at " + ormURL
433: + " for root URL " + rootURL); // NOI18N
434: try {
435: Document document = XMLHelper.parseDocument(stream,
436: ormURL.getFile(), m_loader);
437: list.put(ormURL, document);
438: } finally {
439: try {
440: stream.close();
441: } catch (IOException e) {
442: }
443: }
444: }
445: }
446: return list;
447: }
448:
449: private Map<URL, Document> readExplicitlySpecifiedMappingFiles(
450: boolean throwExceptionOnFail) {
451: Map<URL, Document> list = new HashMap<URL, Document>();
452: final PersistenceUnitInfo puInfo = m_project.getPUInfo();
453: for (String mf : puInfo.getMappingFileNames()) {
454: try {
455: Enumeration<URL> mfURLs = m_loader.getResources(mf);
456: if (mfURLs.hasMoreElements()) {
457: URL nextURL = mfURLs.nextElement();
458: if (mfURLs.hasMoreElements()) {
459: handleORMException(ValidationException
460: .nonUniqueMappingFileName(puInfo
461: .getPersistenceUnitName(), mf),
462: mf, throwExceptionOnFail);
463: }
464: InputStream stream = null;
465: stream = nextURL.openStream();
466: Document document = XMLHelper.parseDocument(stream,
467: nextURL.getFile(), m_loader);
468: list.put(nextURL, document);
469: try {
470: stream.close();
471: } catch (IOException e) {
472: }
473: } else {
474: handleORMException(ValidationException
475: .mappingFileNotFound(puInfo
476: .getPersistenceUnitName(), mf), mf,
477: throwExceptionOnFail);
478: }
479: } catch (IOException e) {
480: handleORMException(PersistenceUnitLoadingException
481: .exceptionLoadingORMXML(mf, e), mf,
482: throwExceptionOnFail);
483: }
484: }
485: return list;
486: }
487:
488: /**
489: * Handle an exception that occured while processing ORM xml
490: */
491: private void handleORMException(RuntimeException e, String mf,
492: boolean throwException) {
493: if (m_session == null) {
494: // Metadata processor is mainly used with a session.
495: // Java SE bootstraping uses some functions such as ORM processing without
496: // a session. In these cases, it is impossible to get the
497: // session to properly handle the exception. As a result we
498: // log an error. The same code will be called later in the bootstrapping
499: // code and the error will be handled then.
500: AbstractSessionLog.getLog().log(SessionLog.CONFIG,
501: EntityManagerSetupImpl.ERROR_LOADING_XML_FILE,
502: new Object[] { mf, e });
503: } else if (!throwException) {
504: // fail quietly
505: m_session.log(SessionLog.CONFIG,
506: SessionLog.EJB_OR_METADATA,
507: EntityManagerSetupImpl.ERROR_LOADING_XML_FILE,
508: new Object[] { mf, e });
509: } else {
510: // fail loudly
511: m_session.handleException(e);
512: }
513: }
514:
515: /**
516: * This method is responsible for discovering all the entity classes
517: * for this PU and adding corresponding MetadataDescriptor in the
518: * MetadataProject. Don't call this method more than once.
519: */
520: public void buildEntityList() {
521: Set<String> classNames = buildEntityClassSetFromAnnotations();
522:
523: // append the list of entity classes that are defined in the XML descriptor
524: classNames.addAll(buildEntityClassSetFromXMLDocuments());
525:
526: m_project.setEntityNames(classNames);
527:
528: // Add a metadata descriptor to the project for every entity class.
529: // Any persistence unit metadata/defaults will be applied
530: for (String className : classNames) {
531: try {
532: m_project.addDescriptor(new MetadataDescriptor(m_loader
533: .loadClass(className)));
534: } catch (ClassNotFoundException e) {
535: AbstractSessionLog.getLog().log(SessionLog.WARNING,
536: "exception_loading_entity_class", className, e);
537: }
538: }
539: }
540:
541: /**
542: * Return a set of class names that are annotated as either @Entity
543: * from the base URL of this PersistenceUnit
544: */
545: private Set<String> buildEntityClassSetFromAnnotations() {
546: Set<String> set = new HashSet<String>();
547: PersistenceUnitInfo puInfo = m_project.getPUInfo();
548:
549: for (String className : puInfo.getManagedClassNames()) {
550: if (PersistenceUnitProcessor.isEntity(className, m_loader,
551: true)) {
552: set.add(className);
553: }
554: }
555:
556: for (URL url : puInfo.getJarFileUrls()) {
557: set.addAll(PersistenceUnitProcessor
558: .getEntityClassNamesFromURL(url, m_loader));
559: }
560:
561: if (!puInfo.excludeUnlistedClasses()) {
562: set.addAll(PersistenceUnitProcessor
563: .getEntityClassNamesFromURL(puInfo
564: .getPersistenceUnitRootUrl(), m_loader));
565: }
566: return set;
567:
568: }
569:
570: /**
571: * INTERNAL:
572: * Return a set of class names for each entity found in the
573: * list of xml descriptor instance documents to be processed by the
574: * MetadataProcessor.
575: *
576: * @return
577: */
578: public Set<String> buildEntityClassSetFromXMLDocuments() {
579: HashSet<String> classSet = new HashSet<String>();
580: for (Map.Entry<URL, Document> urlToDoc : m_project
581: .getMappingFiles().entrySet()) {
582: classSet
583: .addAll(buildEntityClassSetFromXMLDocument(urlToDoc
584: .getValue(), urlToDoc.getKey().getFile(),
585: m_loader));
586: }
587: return classSet;
588: }
589:
590: /**
591: * INTERNAL:
592: * Process metadata found in all the mapping files for this PU.
593: * @see #processMappingFile(org.w3c.dom.Document, String)
594: */
595: public void processMappingFiles() {
596: for (Map.Entry<URL, Document> urlToDocPair : m_project
597: .getMappingFiles().entrySet()) {
598: processMappingFile(urlToDocPair.getValue(), urlToDocPair
599: .getKey().getFile());
600: }
601: }
602:
603: /**
604: * Process the xml and fill in the project. Process the entity-mappings
605: * information then process the entities.
606: */
607: private void processMappingFile(Document document, String fileName) {
608: if (m_project.hasDescriptors()) {
609: XMLHelper helper = new XMLHelper(document, fileName,
610: m_loader);
611:
612: // Process the entity mappings ... this is a crude way of doing
613: // things ... but hey ... the clock is ticking ...
614: MetadataDescriptor desc = m_project.getDescriptors()
615: .iterator().next();
616: XMLClassAccessor dummyAccessor = new XMLClassAccessor(
617: new MetadataClass(desc.getJavaClass()), null,
618: helper, this , desc);
619: dummyAccessor.processEntityMappings();
620:
621: // Process the entity nodes for this xml document.
622: NodeList entityNodes = helper.getNodes(
623: XMLConstants.ENTITY_MAPPINGS, XMLConstants.ENTITY);
624:
625: if (entityNodes != null) {
626: for (int i = 0; i < entityNodes.getLength(); i++) {
627: Node entityNode = entityNodes.item(i);
628: Class entityClass = helper
629: .getClassForNode(entityNode);
630: MetadataDescriptor descriptor = m_project
631: .getDescriptor(entityClass);
632:
633: // Process all descriptors that are in our project.
634: ClassAccessor accessor = descriptor
635: .getClassAccessor();
636:
637: // If there is no accessor on this descriptor then it has not
638: // been processed yet. Create one and process it.
639: if (accessor == null) {
640: accessor = new XMLClassAccessor(
641: new MetadataClass(descriptor
642: .getJavaClass()), entityNode,
643: helper, this , descriptor);
644: descriptor.setClassAccessor(accessor);
645: accessor.process();
646: }
647: }
648: }
649: } else {
650: // WIP - log a warning that we have no entities to process ...
651: }
652: }
653:
654: /**
655: * This method frees up resources acquired by this object.
656: */
657: public void cleanup() {
658: m_project.cleanup();
659: m_loader = null;
660: m_project = null;
661: m_session = null;
662: }
663:
664: /**
665: * Log an untranslated message to the TopLink log at FINER level.
666: * @param msg message to be logged
667: */
668: private void logMessage(String msg) {
669: if (m_session == null) {
670: AbstractSessionLog.getLog().log(SessionLog.FINER, msg);
671: } else {
672: m_session.logMessage(msg);
673: }
674: }
675: }
|