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:
042: package org.openide.util.io;
043:
044: import java.awt.Image;
045: import java.io.ByteArrayOutputStream;
046: import java.io.IOException;
047: import java.io.ObjectOutput;
048: import java.io.ObjectOutputStream;
049: import java.io.OutputStream;
050: import java.util.ArrayList;
051: import java.util.HashSet;
052: import java.util.Iterator;
053: import java.util.List;
054: import java.util.Map;
055: import java.util.Set;
056: import java.util.WeakHashMap;
057: import java.util.logging.Logger;
058: import org.openide.util.WeakSet;
059:
060: // note: keep method NbObjectInputStream.resolveObject
061: // consistent with replaceObject method
062:
063: /** Object output stream that could in the future be smart about saving certain objects.
064: * Also static methods to safely write an object that could cause problems during later deserialization.
065: */
066: public class NbObjectOutputStream extends ObjectOutputStream {
067: private static final String SVUID = "serialVersionUID"; // NOI18N
068: private static final Set<String> alreadyReported = new WeakSet<String>();
069:
070: static {
071: // See below.
072: alreadyReported.add("java.lang.Exception"); // NOI18N
073: alreadyReported.add("java.io.IOException"); // NOI18N
074: alreadyReported.add("java.util.TreeSet"); // NOI18N
075: alreadyReported.add("java.awt.geom.AffineTransform"); // NOI18N
076: }
077:
078: private static Map<String, Boolean> examinedClasses = new WeakHashMap<String, Boolean>(
079: 250);
080: private final List<Class> serializing = new ArrayList<Class>(50);
081:
082: /** Create a new object output.
083: * @param os the underlying output stream
084: * @throws IOException for the usual reasons
085: */
086: public NbObjectOutputStream(OutputStream os) throws IOException {
087: super (os);
088:
089: try {
090: enableReplaceObject(true);
091: } catch (SecurityException ex) {
092: throw (IOException) new IOException(ex.toString())
093: .initCause(ex);
094: }
095: }
096:
097: /*
098: * @param obj is an Object to be checked for replace
099: */
100: public Object replaceObject(Object obj) throws IOException {
101: if (obj instanceof Image) {
102: return null;
103:
104: // [LIGHT]
105: // additional code needed for full version
106: }
107:
108: return super .replaceObject(obj);
109: }
110:
111: /** Writes an object safely to the object output.
112: * Can be read by {@link NbObjectInputStream#readSafely}.
113: * @param oo object output to write to
114: * @param obj the object to write
115: * @exception SafeException if the object simply fails to be serialized
116: * @exception IOException if something more serious fails
117: */
118: public static void writeSafely(ObjectOutput oo, Object obj)
119: throws IOException {
120: ByteArrayOutputStream bos = new ByteArrayOutputStream(200);
121:
122: try {
123: NbObjectOutputStream oos = new NbObjectOutputStream(bos);
124: oos.writeObject(obj);
125: oos.flush();
126: bos.close();
127: } catch (Exception exc) {
128: // exception during safe of the object
129: // encapsulate all exceptions into safe exception
130: oo.writeInt(0);
131: throw new SafeException(exc);
132: }
133:
134: oo.writeInt(bos.size());
135: oo.write(bos.toByteArray());
136: }
137:
138: protected void annotateClass(Class cl) throws IOException {
139: super .annotateClass(cl);
140:
141: if (cl.isArray()) {
142: return;
143: }
144:
145: if (cl.isInterface()) {
146: // TheInterface.class is being serialized, not an instance;
147: // no need for svuid.
148: return;
149: }
150:
151: serializing.add(cl);
152:
153: if (isSerialVersionUIDDeclared(cl)) {
154: return;
155: }
156:
157: if (IOException.class.isAssignableFrom(cl)) {
158: // ObjectOutputStream for some reason stores IOException's that are
159: // thrown during serialization (they are rethrown later maybe?).
160: // It's no problem, just ignore them here.
161: return;
162: }
163:
164: String classname = cl.getName();
165:
166: if (alreadyReported.add(classname)) {
167: Set<Class> serializingUniq = new HashSet<Class>();
168: StringBuffer b = new StringBuffer("Serializable class "); // NOI18N
169: b.append(classname);
170: b
171: .append(" does not declare serialVersionUID field. Encountered while storing: ["); // NOI18N
172:
173: Iterator it = serializing.iterator();
174: boolean first = true;
175:
176: while (it.hasNext()) {
177: Class c = (Class) it.next();
178:
179: if ((c != cl) && serializingUniq.add(c)) {
180: if (first) {
181: first = false;
182: } else {
183: b.append(", "); // NOI18N
184: }
185:
186: b.append(c.getName());
187: }
188: }
189:
190: b
191: .append("] See also http://www.netbeans.org/issues/show_bug.cgi?id=19915"); // NOI18N
192:
193: String file = System
194: .getProperty("InstanceDataObject.current.file"); // NOI18N
195:
196: if ((file != null) && (file.length() > 0)) {
197: b.append(" [may have been writing "); // NOI18N
198: b.append(file);
199: b.append("]"); // NOI18N
200: }
201:
202: Logger.getLogger(NbObjectOutputStream.class.getName())
203: .warning(b.toString());
204: }
205: }
206:
207: private static boolean isSerialVersionUIDDeclared(Class clazz) {
208: String classname = clazz.getName();
209: Boolean okay = examinedClasses.get(classname);
210:
211: if (okay == null) {
212: if (classname.equals("java.util.HashSet")
213: || classname.equals("java.util.ArrayList")) { // NOI18N
214: okay = Boolean.TRUE;
215: } else {
216: okay = Boolean.FALSE;
217:
218: java.lang.reflect.Field[] flds = clazz
219: .getDeclaredFields();
220:
221: for (int i = 0; i < flds.length; i++) {
222: if (flds[i].getName().equals(SVUID)) {
223: okay = Boolean.TRUE;
224:
225: break;
226: }
227: }
228: }
229:
230: examinedClasses.put(clazz.getName(), okay);
231: }
232:
233: return okay.booleanValue();
234: }
235: }
|