001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.util;
006:
007: import com.tc.object.appevent.NonPortableFieldSetContext;
008: import com.tc.object.appevent.NonPortableRootContext;
009: import com.tc.text.NonPortableReasonFormatter;
010:
011: import java.io.IOException;
012: import java.io.ObjectOutputStream;
013: import java.io.Serializable;
014: import java.text.MessageFormat;
015: import java.util.ArrayList;
016: import java.util.Collection;
017: import java.util.Iterator;
018: import java.util.LinkedList;
019: import java.util.List;
020: import java.util.ResourceBundle;
021:
022: /**
023: * Encapsulate why something is non-portable and build nice error messages
024: * for printing when that occurs.
025: */
026: public class NonPortableReason implements Serializable {
027:
028: private static final long serialVersionUID = 8149536931286184441L;
029:
030: public static final byte UNDEFINED = 0x00;
031:
032: public static final byte CLASS_NOT_ADAPTABLE = 0x01;
033: public static final byte SUPER_CLASS_NOT_ADAPTABLE = 0x02;
034: public static final byte SUBCLASS_OF_LOGICALLY_MANAGED_CLASS = 0x03;
035: public static final byte CLASS_NOT_IN_BOOT_JAR = 0x04;
036: public static final byte CLASS_NOT_INCLUDED_IN_CONFIG = 0x05;
037: public static final byte SUPER_CLASS_NOT_INSTRUMENTED = 0x06;
038: public static final byte TEST_REASON = 0x07;
039:
040: private static final byte LAST_DEFINED = 0x07;
041:
042: private final String className;
043: private final List nonBootJarClasses;
044: private final List bootJarClasses;
045: private final Collection details;
046: private final byte reason;
047: private transient String detailedReason;
048: private transient String instructions;
049: private String message;
050: private String ultimateNonPortableFieldName;
051:
052: /**
053: * @param clazz The class that is non-portable
054: * @param reasonCode The reason why it is non-portable
055: */
056: public NonPortableReason(Class clazz, byte reasonCode) {
057: this (clazz.getName(), reasonCode);
058: }
059:
060: /**
061: * @param className The class that is non-portable
062: * @param reasonCode The reason why it is non-portable
063: */
064: public NonPortableReason(String className, byte reasonCode) {
065: this .className = className;
066: this .reason = reasonCode;
067: this .details = new LinkedList();
068: nonBootJarClasses = new LinkedList();
069: bootJarClasses = new LinkedList();
070: }
071:
072: /**
073: * @return Class name
074: */
075: public String getClassName() {
076: return className;
077: }
078:
079: /**
080: * @return Detailed reason why something is non-portable
081: */
082: public synchronized String getDetailedReason() {
083: if (detailedReason == null) {
084: detailedReason = constructDetailedReason();
085: }
086: return detailedReason;
087: }
088:
089: /**
090: * @return Instructions on how to correct the problem
091: */
092: public synchronized String getInstructions() {
093: if (instructions == null) {
094: instructions = constructInstructions();
095: }
096: return instructions;
097: }
098:
099: private void writeObject(ObjectOutputStream out) throws IOException {
100: checkSanity();
101: out.defaultWriteObject();
102: }
103:
104: /**
105: * Add detail to the reason
106: * @param label The label
107: * @param value The value
108: */
109: public void addDetail(String label, String value) {
110: details.add(new NonPortableDetail(label, value));
111: }
112:
113: private String constructDetailedReason() {
114: // XXX: This preamble thing is dumb. This messaging is constructed without the full context. I wasn't able to
115: // refactor that problem away in time. --Orion 04/26/06
116: boolean hasPreamble = message != null;
117: StringBuffer sb = (message == null ? new StringBuffer()
118: : new StringBuffer(message));
119:
120: switch (reason) {
121: case CLASS_NOT_ADAPTABLE:
122: if (hasPreamble) {
123: sb.append(" This unshareable class is a");
124: } else {
125: sb.append("Attempted to share a");
126: }
127: sb
128: .append(" JVM- or host machine-specific resource. Please ensure that instances of this class"
129: + " don't enter the shared object graph.");
130: addDetail("Unshareable class", className);
131: break;
132: case SUPER_CLASS_NOT_ADAPTABLE:
133: if (hasPreamble) {
134: sb.append(" This unshareable class is a");
135: } else {
136: sb
137: .append("Attempted to share an instance of a class that is a");
138: }
139: sb
140: .append(" subclass of a JVM- or host machine-specific resource.");
141: sb
142: .append(" Please either modify the class hierarchy or ensure that instances of this class don't"
143: + " enter the shared object graph.");
144: addDetail("Unshareable superclass names",
145: getErroneousSuperClassNames());
146: break;
147: case SUBCLASS_OF_LOGICALLY_MANAGED_CLASS:
148: if (hasPreamble) {
149: sb.append(" This unshareable class");
150: } else {
151: sb
152: .append("Attempted to share an instance of a class which");
153: }
154: sb.append(" has a logically-managed superclass.");
155: sb
156: .append(" Subclasses of logically-managed classes cannot be shared. Please either");
157: sb
158: .append(" modify the class hierarchy or ensure that instances of this class"
159: + " don't enter the shared object graph.");
160: addDetail("Unshareable class", className);
161: addDetail("Logically-managed superclass names",
162: getErroneousSuperClassNames());
163: break;
164: case CLASS_NOT_IN_BOOT_JAR:
165: if (hasPreamble) {
166: sb.append(" This unshareable class");
167: } else {
168: sb
169: .append("Attempted to share an instance of a class which");
170: }
171: if (!bootJarClasses.isEmpty()) {
172: sb
173: .append(" must be in the DSO boot jar. It also has superclasses which must be in the DSO"
174: + " boot jar. Please add all of these classes to the boot jar configuration and re-create"
175: + " the DSO boot jar.");
176: List classes = new ArrayList();
177: classes.addAll(bootJarClasses);
178: classes.add(className);
179: addDetail("Classes to add to boot jar",
180: csvList(classes));
181: } else {
182: sb
183: .append(" must be in the DSO boot jar. Please add this class to the boot jar configuration"
184: + " and re-create the DSO boot jar.");
185: addDetail("Class to add to boot jar", className);
186: }
187:
188: break;
189: case SUPER_CLASS_NOT_INSTRUMENTED:
190: if (hasPreamble) {
191: sb.append(" This unshareable class has");
192: } else {
193: sb
194: .append("Attempted to share an instance of a class which has");
195: }
196: boolean plural = (bootJarClasses.size() + nonBootJarClasses
197: .size()) > 1;
198: sb.append(plural ? " super-classes" : " a super-class");
199: sb
200: .append(" that"
201: + (plural ? " are" : " is")
202: + " uninstrumented."
203: + " Subclasses of uninstrumented classes cannot be shared.");
204:
205: addDetail("Unshareable class", className);
206: if (!bootJarClasses.isEmpty()) {
207: addDetail("Classes to add to boot jar",
208: csvList(bootJarClasses));
209: }
210: if (!nonBootJarClasses.isEmpty()) {
211: addDetail(
212: "Classes to add to the <includes> configuration",
213: csvList(nonBootJarClasses));
214: }
215: break;
216: case CLASS_NOT_INCLUDED_IN_CONFIG:
217: if (hasPreamble) {
218: sb.append(" This unshareable class");
219: } else {
220: sb
221: .append("Attempted to share an instance of a class which");
222: }
223: sb
224: .append(" has not been included for sharing in the configuration.");
225: if (!nonBootJarClasses.isEmpty()) {
226: List classes = new LinkedList();
227: classes.add(className);
228: classes.addAll(nonBootJarClasses);
229: addDetail("Non-included classes", csvList(classes));
230: } else {
231: addDetail("Non-included class", className);
232: }
233: if (!bootJarClasses.isEmpty()) {
234: if (bootJarClasses.size() == 1) {
235: addDetail("Class to add to boot jar",
236: csvList(bootJarClasses));
237: } else {
238: addDetail("Classes to add to boot jar",
239: csvList(bootJarClasses));
240: }
241: }
242: break;
243: case TEST_REASON:
244: break;
245: default:
246: throw new AssertionError("Unknown reason: " + reason);
247: }
248: return sb.toString();
249: }
250:
251: private String constructInstructions() {
252: StringBuffer sb = new StringBuffer();
253: switch (reason) {
254: case CLASS_NOT_ADAPTABLE: {
255: NonPortableDetail detail = findDetailByLabel(NonPortableRootContext.ROOT_NAME_LABEL);
256: final boolean isRoot = detail != null;
257: if (detail == null) {
258: detail = findDetailByLabel(NonPortableFieldSetContext.FIELD_NAME_LABEL);
259: }
260: sb.append(Messages.classNotAdaptableInstructions(
261: detail != null ? detail.getValue() : null,
262: className, isRoot));
263: }
264: break;
265: case SUPER_CLASS_NOT_ADAPTABLE: {
266: NonPortableDetail detail = findDetailByLabel(NonPortableRootContext.ROOT_NAME_LABEL);
267: final boolean isRoot = detail != null;
268: if (detail == null) {
269: detail = findDetailByLabel(NonPortableFieldSetContext.FIELD_NAME_LABEL);
270: }
271: sb.append(Messages.super ClassNotAdaptableInstructions(
272: detail != null ? detail.getValue() : null,
273: className, getErroneousSuperClassNames(), isRoot));
274: }
275: break;
276: case SUBCLASS_OF_LOGICALLY_MANAGED_CLASS: {
277: NonPortableDetail detail = findDetailByLabel(NonPortableRootContext.ROOT_NAME_LABEL);
278: final boolean isRoot = detail != null;
279: if (detail == null) {
280: detail = findDetailByLabel(NonPortableFieldSetContext.FIELD_NAME_LABEL);
281: }
282: sb.append(Messages
283: .subclassOfLogicallyManagedClassInstructions(
284: detail != null ? detail.getValue() : null,
285: className, getErroneousSuperClassNames(),
286: isRoot));
287: }
288: break;
289: case CLASS_NOT_IN_BOOT_JAR:
290: List classes = new LinkedList();
291: classes.addAll(bootJarClasses);
292: classes.add(className);
293: sb.append(Messages.classNotInBootJarInstructions(classes));
294: break;
295: case CLASS_NOT_INCLUDED_IN_CONFIG:
296: List normalClasses = new LinkedList();
297: normalClasses.add(className);
298: if (nonBootJarClasses != null) {
299: normalClasses.addAll(nonBootJarClasses);
300: }
301: sb.append(Messages.classNotIncludedInConfigInstructions(
302: normalClasses, bootJarClasses));
303: break;
304: case SUPER_CLASS_NOT_INSTRUMENTED:
305: sb.append(Messages.super ClassNotInstrumentedInstructions(
306: nonBootJarClasses, bootJarClasses));
307: break;
308: case TEST_REASON:
309: sb.append("instructions");
310: break;
311: }
312: return sb.toString();
313: }
314:
315: /**
316: * Check whether this reason knows the field name referring to the non-portable object.
317: * @return True if has field name
318: */
319: public boolean hasUltimateNonPortableFieldName() {
320: return ultimateNonPortableFieldName != null;
321: }
322:
323: /**
324: * Set the name of the field holding the nonportable object.
325: * @param name Name of the field
326: */
327: public void setUltimateNonPortableFieldName(String name) {
328: addDetail("Referring field", name);
329: ultimateNonPortableFieldName = name;
330: }
331:
332: /**
333: * @return the field holding the non-portable object.
334: */
335: public String getUltimateNonPortableFieldName() {
336: return ultimateNonPortableFieldName;
337: }
338:
339: private String getErroneousSuperClassNames() {
340: Collection super s = new LinkedList(nonBootJarClasses);
341: super s.addAll(bootJarClasses);
342: return csvList(super s);
343: }
344:
345: private static String csvList(Collection list) {
346: StringBuffer sb = new StringBuffer();
347: for (Iterator i = list.iterator(); i.hasNext();) {
348: sb.append(i.next());
349: if (i.hasNext()) {
350: sb.append(", ");
351: }
352: }
353:
354: return sb.toString();
355: }
356:
357: private void checkSanity() {
358: if (reason <= UNDEFINED || reason > LAST_DEFINED) {
359: // setReason() called with wrong values.
360: throw new AssertionError(
361: "Please specify the reason for Non-portability by calling setReason() with one of the defined reasons.");
362: }
363: if ((reason == SUBCLASS_OF_LOGICALLY_MANAGED_CLASS
364: || reason == SUPER_CLASS_NOT_ADAPTABLE || reason == SUPER_CLASS_NOT_INSTRUMENTED)
365: && ((nonBootJarClasses.size() == 0) && (bootJarClasses
366: .size() == 0))) {
367: // addErroneousSuperClass() need to be called.
368: throw new AssertionError(
369: "Please add erroneous super classes by calling addErroneousSuperClass()");
370: }
371: }
372:
373: /**
374: * @return Reason code
375: */
376: public byte getReason() {
377: return reason;
378: }
379:
380: /**
381: * Add erroneous super class
382: * @param superClass Super class that is non-portable
383: */
384: public void addErroneousSuperClass(Class super Class) {
385: if (super Class.getClassLoader() == null) {
386: bootJarClasses.add(super Class.getName());
387: } else {
388: nonBootJarClasses.add(super Class.getName());
389: }
390: }
391:
392: /**
393: * @return All erroneous super classes not in the boot jar
394: */
395: public List getErroneousSuperClasses() {
396: return nonBootJarClasses;
397: }
398:
399: /**
400: * @return All erroneous super classes in the boot jar
401: */
402: public List getErroneousBootJarSuperClasses() {
403: return bootJarClasses;
404: }
405:
406: public String toString() {
407: return getDetailedReason();
408: }
409:
410: /**
411: * @param msg The message
412: */
413: public void setMessage(String msg) {
414: message = msg;
415: }
416:
417: /**
418: * @return The message
419: */
420: public String getMessage() {
421: return message;
422: }
423:
424: /**
425: * Accept a formatter for message formatting. This method will walk
426: * the reason text, details, and instructions.
427: * @param formatter Formatter to help formatting the reason
428: */
429: public void accept(NonPortableReasonFormatter formatter) {
430: formatter.formatReasonText(getDetailedReason());
431: for (Iterator i = details.iterator(); i.hasNext();) {
432: formatter.formatDetail((NonPortableDetail) i.next());
433: }
434: formatter.formatInstructionsText(getInstructions());
435: }
436:
437: private NonPortableDetail findDetailByLabel(final String label) {
438: for (Iterator pos = details.iterator(); pos.hasNext();) {
439: NonPortableDetail detail = (NonPortableDetail) pos.next();
440: if (label.equals(detail.getLabel())) {
441: return detail;
442: }
443: }
444: return null;
445: }
446:
447: private static final class Messages {
448:
449: private static final ResourceBundle rb = ResourceBundle
450: .getBundle(NonPortableReason.class.getName());
451:
452: private static final String CLASS_NOT_ADAPTABLE_ROOT_INSTRUCTIONS_KEY = "classNotAdaptable.root.instructions";
453: private static final String CLASS_NOT_ADAPTABLE_FIELD_INSTRUCTIONS_KEY = "classNotAdaptable.field.instructions";
454:
455: private static final String SUPER_CLASS_NOT_ADAPTABLE_ROOT_INSTRUCTIONS_KEY = "superClassNotAdaptable.root.instructions";
456: private static final String SUPER_CLASS_NOT_ADAPTABLE_FIELD_INSTRUCTIONS_KEY = "superClassNotAdaptable.field.instructions";
457:
458: private static final String SUBCLASS_OF_LOGICALLY_MANAGED_CLASS_ROOT_INSTRUCTIONS_KEY = "logicallyManagedSuperClass.root.instructions";
459: private static final String SUBCLASS_OF_LOGICALLY_MANAGED_CLASS_FIELD_INSTRUCTIONS_KEY = "logicallyManagedSuperClass.field.instructions";
460:
461: private static final String CLASS_NOT_IN_BOOT_JAR_CLASS_KEY = "classNotInBootJar.class";
462: private static final String CLASS_NOT_IN_BOOT_JAR_INSTRUCTIONS_KEY = "classNotInBootJar.instructions";
463:
464: private static final String CLASS_NOT_INCLUDED_IN_CONFIG_HEADER_KEY = "classNotIncludedInConfig.header";
465: private static final String CLASS_NOT_INCLUDED_IN_CONFIG_NON_BOOTJAR_CLASS_KEY = "classNotIncludedInConfig.non-bootjar.class";
466: private static final String CLASS_NOT_INCLUDED_IN_CONFIG_NON_BOOTJAR_INSTRUCTIONS_KEY = "classNotIncludedInConfig.non-bootjar.instructions";
467: private static final String CLASS_NOT_INCLUDED_IN_CONFIG_BOOTJAR_CLASS_KEY = "classNotIncludedInConfig.bootjar.class";
468: private static final String CLASS_NOT_INCLUDED_IN_CONFIG_BOOTJAR_INSTRUCTIONS_KEY = "classNotIncludedInConfig.bootjar.instructions";
469: private static final String CLASS_NOT_INCLUDED_IN_CONFIG_FOOTER_KEY = "classNotIncludedInConfig.footer";
470:
471: private static final String SUPER_CLASS_NOT_INSTRUMENTED_HEADER_KEY = "superClassNotInstrumented.header";
472: private static final String SUPER_CLASS_NOT_INSTRUMENTED_NON_BOOTJAR_CLASS_KEY = "superClassNotInstrumented.non-bootjar.class";
473: private static final String SUPER_CLASS_NOT_INSTRUMENTED_NON_BOOTJAR_INSTRUCTIONS_KEY = "superClassNotInstrumented.non-bootjar.instructions";
474: private static final String SUPER_CLASS_NOT_INSTRUMENTED_BOOTJAR_CLASS_KEY = "superClassNotInstrumented.bootjar.class";
475: private static final String SUPER_CLASS_NOT_INSTRUMENTED_BOOTJAR_INSTRUCTIONS_KEY = "superClassNotInstrumented.bootjar.instructions";
476: private static final String SUPER_CLASS_NOT_INSTRUMENTED_FOOTER_KEY = "superClassNotInstrumented.footer";
477:
478: static String classNotAdaptableInstructions(String fieldName,
479: String nonAdaptableClassName, boolean isRootField) {
480: return MessageFormat
481: .format(
482: rb
483: .getString(isRootField ? CLASS_NOT_ADAPTABLE_ROOT_INSTRUCTIONS_KEY
484: : CLASS_NOT_ADAPTABLE_FIELD_INSTRUCTIONS_KEY),
485: new Object[] { fieldName,
486: nonAdaptableClassName });
487: }
488:
489: static String super ClassNotAdaptableInstructions(
490: String fieldName, String nonAdaptableSubclass,
491: String nonAdaptableSuperClasses, boolean isRootField) {
492: return MessageFormat
493: .format(
494: rb
495: .getString(isRootField ? SUPER_CLASS_NOT_ADAPTABLE_ROOT_INSTRUCTIONS_KEY
496: : SUPER_CLASS_NOT_ADAPTABLE_FIELD_INSTRUCTIONS_KEY),
497: new Object[] { fieldName,
498: nonAdaptableSubclass,
499: nonAdaptableSuperClasses });
500: }
501:
502: static String subclassOfLogicallyManagedClassInstructions(
503: String fieldName, String nonAdaptableSubclass,
504: String logicallyManagedSuperClasses, boolean isRootField) {
505: return MessageFormat
506: .format(
507: rb
508: .getString(isRootField ? SUBCLASS_OF_LOGICALLY_MANAGED_CLASS_ROOT_INSTRUCTIONS_KEY
509: : SUBCLASS_OF_LOGICALLY_MANAGED_CLASS_FIELD_INSTRUCTIONS_KEY),
510: new Object[] { fieldName,
511: nonAdaptableSubclass,
512: logicallyManagedSuperClasses });
513: }
514:
515: static String classNotInBootJarInstructions(List classes) {
516: final StringBuffer classesMsg = new StringBuffer();
517: for (Iterator pos = classes.iterator(); pos.hasNext();) {
518: classesMsg.append(MessageFormat.format(rb
519: .getString(CLASS_NOT_IN_BOOT_JAR_CLASS_KEY),
520: new Object[] { pos.next() }));
521: }
522: return MessageFormat.format(rb
523: .getString(CLASS_NOT_IN_BOOT_JAR_INSTRUCTIONS_KEY),
524: new Object[] { classesMsg });
525: }
526:
527: static String classNotIncludedInConfigInstructions(
528: List normalClassNames, List bootJarClassNames) {
529: final StringBuffer instructions = new StringBuffer(
530: MessageFormat
531: .format(
532: rb
533: .getString(CLASS_NOT_INCLUDED_IN_CONFIG_HEADER_KEY),
534: null));
535: if (normalClassNames != null && !normalClassNames.isEmpty()) {
536: final StringBuffer classList = new StringBuffer();
537: for (Iterator pos = normalClassNames.iterator(); pos
538: .hasNext();) {
539: classList
540: .append(MessageFormat
541: .format(
542: rb
543: .getString(CLASS_NOT_INCLUDED_IN_CONFIG_NON_BOOTJAR_CLASS_KEY),
544: new Object[] { pos.next() }));
545: }
546: instructions
547: .append(MessageFormat
548: .format(
549: rb
550: .getString(CLASS_NOT_INCLUDED_IN_CONFIG_NON_BOOTJAR_INSTRUCTIONS_KEY),
551: new Object[] { classList }));
552: }
553: if (bootJarClassNames != null
554: && !bootJarClassNames.isEmpty()) {
555: final StringBuffer bootJarClassList = new StringBuffer();
556: for (Iterator pos = bootJarClassNames.iterator(); pos
557: .hasNext();) {
558: bootJarClassList
559: .append(MessageFormat
560: .format(
561: rb
562: .getString(CLASS_NOT_INCLUDED_IN_CONFIG_BOOTJAR_CLASS_KEY),
563: new Object[] { pos.next() }));
564: }
565: instructions
566: .append(MessageFormat
567: .format(
568: rb
569: .getString(CLASS_NOT_INCLUDED_IN_CONFIG_BOOTJAR_INSTRUCTIONS_KEY),
570: new Object[] { bootJarClassList }));
571: }
572: instructions
573: .append(MessageFormat
574: .format(
575: rb
576: .getString(CLASS_NOT_INCLUDED_IN_CONFIG_FOOTER_KEY),
577: null));
578: return instructions.toString();
579: }
580:
581: static String super ClassNotInstrumentedInstructions(
582: List normalClassNames, List bootJarClassNames) {
583: final StringBuffer instructions = new StringBuffer(
584: MessageFormat
585: .format(
586: rb
587: .getString(SUPER_CLASS_NOT_INSTRUMENTED_HEADER_KEY),
588: null));
589: if (normalClassNames != null && !normalClassNames.isEmpty()) {
590: final StringBuffer classList = new StringBuffer();
591: for (Iterator pos = normalClassNames.iterator(); pos
592: .hasNext();) {
593: classList
594: .append(MessageFormat
595: .format(
596: rb
597: .getString(SUPER_CLASS_NOT_INSTRUMENTED_NON_BOOTJAR_CLASS_KEY),
598: new Object[] { pos.next() }));
599: }
600: instructions
601: .append(MessageFormat
602: .format(
603: rb
604: .getString(SUPER_CLASS_NOT_INSTRUMENTED_NON_BOOTJAR_INSTRUCTIONS_KEY),
605: new Object[] { classList }));
606: }
607: if (bootJarClassNames != null
608: && !bootJarClassNames.isEmpty()) {
609: final StringBuffer bootJarClassList = new StringBuffer();
610: for (Iterator pos = bootJarClassNames.iterator(); pos
611: .hasNext();) {
612: bootJarClassList
613: .append(MessageFormat
614: .format(
615: rb
616: .getString(SUPER_CLASS_NOT_INSTRUMENTED_BOOTJAR_CLASS_KEY),
617: new Object[] { pos.next() }));
618: }
619: instructions
620: .append(MessageFormat
621: .format(
622: rb
623: .getString(SUPER_CLASS_NOT_INSTRUMENTED_BOOTJAR_INSTRUCTIONS_KEY),
624: new Object[] { bootJarClassList }));
625: }
626: instructions
627: .append(MessageFormat
628: .format(
629: rb
630: .getString(SUPER_CLASS_NOT_INSTRUMENTED_FOOTER_KEY),
631: null));
632: return instructions.toString();
633: }
634:
635: }
636:
637: }
|