001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.openide.util.io;
042:
043: import org.openide.util.Lookup;
044: import org.openide.util.Utilities;
045:
046: import java.io.ByteArrayInputStream;
047: import java.io.IOException;
048: import java.io.InputStream;
049: import java.io.ObjectInput;
050: import java.io.ObjectInputStream;
051: import java.io.ObjectStreamClass;
052:
053: import java.lang.reflect.InvocationTargetException;
054: import org.openide.util.Exceptions;
055:
056: // note: keep method resolveObject consistent with NbObjectOutputStream.replaceObject
057:
058: /** Controlled deserialization stream using the system class loader.
059: * Employs the classloader available from lookup (currently that used for modules).
060: * Also contains static methods to safely read objects that might have problems
061: * during deserialization that should not corrupt the stream. The stream also provides
062: * support for changing name of stored classes.
063: *
064: * @see #readClassDescriptor
065: */
066: public class NbObjectInputStream extends ObjectInputStream {
067: /** Create a new object input.
068: * @param is underlying input stream
069: * @throws IOException for the usual reasons
070: */
071: public NbObjectInputStream(InputStream is) throws IOException {
072: super (is);
073:
074: try {
075: enableResolveObject(true);
076: } catch (SecurityException ex) {
077: throw new IOException(ex.toString());
078: }
079: }
080:
081: /* Uses NetBeans module classloader to load the class.
082: * @param v description of the class to load
083: */
084: protected Class resolveClass(ObjectStreamClass v)
085: throws IOException, ClassNotFoundException {
086: ClassLoader cl = getNBClassLoader();
087:
088: try {
089: return Class.forName(v.getName(), false, cl);
090: } catch (ClassNotFoundException cnfe) {
091: String msg = "Offending classloader: " + cl; // NOI18N
092: Exceptions.attachMessage(cnfe, msg);
093: throw cnfe;
094: }
095: }
096:
097: /** Lazy create default NB classloader for use during deserialization. */
098: private static ClassLoader getNBClassLoader() {
099: ClassLoader c = Lookup.getDefault().lookup(ClassLoader.class);
100:
101: return (c != null) ? c : ClassLoader.getSystemClassLoader();
102: }
103:
104: /** Provides a special handling for renaming of serialized classes.
105: * <P>
106: * Often, as the time goes the serialized classes evolve. They can be moved
107: * to new packages, renamed or changed (by a mistake) to not reflect the
108: * version of class stored in previous sessions.
109: * <P>
110: * This method deals with some of this incompatibilites and provides the
111: * module owners a way how to fix some of them.
112: * <P>
113: * When a class is read, the <link>Utilities.translate</link> is consulted
114: * to find out what whether the name of the class is listed there and
115: * what new value is assigned to it. This allows complete rename of the
116: * serialized class. For example:
117: * <code>org.netbeans.core.NbMainExplorer</code>
118: * can be renamed to
119: * <code>org.netbeans.core.ui.NbExp</code> - of course supposing that
120: * the new class is able to read the serialized fields of the old one.
121: * <P>
122: * Another useful feature of this method is the ability to supress wrong
123: * <code>serialVersionUID</code>. This was causing us a lot of problems,
124: * because people were forgetting to specify the <code>serialVersionUID</code>
125: * field in their sources and then it was hard to recover from it. Right
126: * now we have a solution: Just use <link>Utilities.translate</link> framework
127: * to assing your class <code>org.yourpackage.YourClass</code> the same
128: * name as it had e.g. <code>org.yourpackage.YourClass</code>. This will
129: * be interpreted by this method as a hit to suppress <code>serialVersionUID</code>
130: * and the <code>NbObjectInputStream</code> will ignore its value.
131: * <P>
132: * Please see <link>Utilities.translate</link> to learn how your module
133: * can provide list of classes that changed name or want to suppress <code>serialVersionUID</code>.
134: *
135: */
136: protected ObjectStreamClass readClassDescriptor()
137: throws IOException, ClassNotFoundException {
138: ObjectStreamClass ose = super .readClassDescriptor();
139:
140: String name = ose.getName();
141: String newN = Utilities.translate(name);
142:
143: if (name == newN) {
144: // no translation
145: return ose;
146: }
147:
148: // otherwise reload the ObjectStreamClass to contain the local copy
149: ClassLoader cl = getNBClassLoader();
150: Class clazz = Class.forName(newN, false, cl);
151:
152: ObjectStreamClass newOse = ObjectStreamClass.lookup(clazz);
153:
154: // #28021 - it is possible that lookup return null. In that case the conversion
155: // table contains class which is not Serializable or Externalizable.
156: if (newOse == null) {
157: throw new java.io.NotSerializableException(newN);
158: }
159:
160: return newOse;
161: }
162:
163: /** Reads an object from the given object input.
164: * The object had to be saved by the {@link NbObjectOutputStream#writeSafely} method.
165: *
166: * @param oi object input
167: * @return the read object
168: * @exception IOException if IO error occured
169: * @exception SafeException if the operation failed but the stream is ok
170: * for further reading
171: */
172: public static Object readSafely(ObjectInput oi) throws IOException {
173: int size = oi.readInt();
174: byte[] byteArray = new byte[size];
175: oi.readFully(byteArray, 0, size);
176:
177: try {
178: ByteArrayInputStream bis = new ByteArrayInputStream(
179: byteArray);
180: NbObjectInputStream ois = new NbObjectInputStream(bis);
181: Object obj = ois.readObject();
182: bis.close();
183:
184: return obj;
185: } catch (Exception exc) {
186: // encapsulate all exceptions into safe exception
187: throw new SafeException(exc);
188: } catch (LinkageError le) {
189: throw new SafeException(new InvocationTargetException(le));
190: }
191: }
192:
193: /** Skips an object from the given object input without loading it.
194: * The object had to be saved by the {@link NbObjectOutputStream#writeSafely} method.
195: *
196: * @param oi object input
197: * @exception IOException if an I/O error occurred
198: */
199: public static void skipSafely(ObjectInput oi) throws IOException {
200: int size = oi.readInt();
201: oi.skip(size);
202: }
203: }
|