/* * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.sql.rowset.serial; import java.io.*; import java.lang.reflect.*; import java.util.Arrays; import java.util.Vector; import javax.sql.rowset.RowSetWarning; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import sun.reflect.misc.ReflectUtil; /** * A serializable mapping in the Java programming language of an SQL * <code>JAVA_OBJECT</code> value. Assuming the Java object * implements the <code>Serializable</code> interface, this class simply wraps the * serialization process. * <P> * If however, the serialization is not possible because * the Java object is not immediately serializable, this class will * attempt to serialize all non-static members to permit the object * state to be serialized. * Static or transient fields cannot be serialized; an attempt to serialize * them will result in a <code>SerialException</code> object being thrown. * * <h3> Thread safety </h3> * * A SerialJavaObject is not safe for use by multiple concurrent threads. If a * SerialJavaObject is to be used by more than one thread then access to the * SerialJavaObject should be controlled by appropriate synchronization. * * @author Jonathan Bruce * @since 1.5 */ public class SerialJavaObject implements Serializable, Cloneable { /** * Placeholder for object to be serialized. */ private Object obj; /** * Placeholder for all fields in the <code>JavaObject</code> being serialized. */ private transient Field[] fields; /** * Constructor for <code>SerialJavaObject</code> helper class. * * @param obj the Java <code>Object</code> to be serialized * @throws SerialException if the object is found not to be serializable */ public SerialJavaObject(Object obj) throws SerialException { // if any static fields are found, an exception // should be thrown // get Class. Object instance should always be available Class<?> c = obj.getClass(); // determine if object implements Serializable i/f if (!(obj instanceof java.io.Serializable)) { setWarning(new RowSetWarning("Warning, the object passed to the constructor does not implement Serializable")); } // can only determine public fields (obviously). If // any of these are static, this should invalidate // the action of attempting to persist these fields // in a serialized form fields = c.getFields(); if (hasStaticFields(fields)) { throw new SerialException("Located static fields in " + "object instance. Cannot serialize"); } this.obj = obj; } /** * Returns an <code>Object</code> that is a copy of this <code>SerialJavaObject</code> * object. * * @return a copy of this <code>SerialJavaObject</code> object as an * <code>Object</code> in the Java programming language * @throws SerialException if the instance is corrupt */ public Object getObject() throws SerialException { return this.obj; } /** * Returns an array of <code>Field</code> objects that contains each * field of the object that this helper class is serializing. * * @return an array of <code>Field</code> objects * @throws SerialException if an error is encountered accessing * the serialized object * @throws SecurityException If a security manager, <i>s</i>, is present * and the caller's class loader is not the same as or an * ancestor of the class loader for the class of the * {@linkplain #getObject object} being serialized * and invocation of {@link SecurityManager#checkPackageAccess * s.checkPackageAccess()} denies access to the package * of that class. * @see Class#getFields */ @CallerSensitive public Field[] getFields() throws SerialException { if (fields != null) { Class<?> c = this.obj.getClass(); SecurityManager sm = System.getSecurityManager(); if (sm != null) { /* * Check if the caller is allowed to access the specified class's package. * If access is denied, throw a SecurityException. */ Class<?> caller = Reflection.getCallerClass(); if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), c.getClassLoader())) { ReflectUtil.checkPackageAccess(c); } } return c.getFields(); } else { throw new SerialException("SerialJavaObject does not contain" + " a serialized object instance"); } } /** * The identifier that assists in the serialization of this * <code>SerialJavaObject</code> object. */ static final long serialVersionUID = -1465795139032831023L; /** * A container for the warnings issued on this <code>SerialJavaObject</code> * object. When there are multiple warnings, each warning is chained to the * previous warning. */ Vector<RowSetWarning> chain; /** * Compares this SerialJavaObject to the specified object. * The result is {@code true} if and only if the argument * is not {@code null} and is a {@code SerialJavaObject} * object that is identical to this object * * @param o The object to compare this {@code SerialJavaObject} against * * @return {@code true} if the given object represents a {@code SerialJavaObject} * equivalent to this SerialJavaObject, {@code false} otherwise * */ public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof SerialJavaObject) { SerialJavaObject sjo = (SerialJavaObject) o; return obj.equals(sjo.obj); } return false; } /** * Returns a hash code for this SerialJavaObject. The hash code for a * {@code SerialJavaObject} object is taken as the hash code of * the {@code Object} it stores * * @return a hash code value for this object. */ public int hashCode() { return 31 + obj.hashCode(); } /** * Returns a clone of this {@code SerialJavaObject}. * * @return a clone of this SerialJavaObject */ public Object clone() { try { SerialJavaObject sjo = (SerialJavaObject) super.clone(); sjo.fields = Arrays.copyOf(fields, fields.length); if (chain != null) sjo.chain = new Vector<>(chain); return sjo; } catch (CloneNotSupportedException ex) { // this shouldn't happen, since we are Cloneable throw new InternalError(); } } /** * Registers the given warning. */ private void setWarning(RowSetWarning e) { if (chain == null) { chain = new Vector<>(); } chain.add(e); } /** * readObject is called to restore the state of the {@code SerialJavaObject} * from a stream. */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields1 = s.readFields(); @SuppressWarnings("unchecked") Vector<RowSetWarning> tmp = (Vector<RowSetWarning>)fields1.get("chain", null); if (tmp != null) chain = new Vector<>(tmp); obj = fields1.get("obj", null); if (obj != null) { fields = obj.getClass().getFields(); if(hasStaticFields(fields)) throw new IOException("Located static fields in " + "object instance. Cannot serialize"); } else { throw new IOException("Object cannot be null!"); } } /** * writeObject is called to save the state of the {@code SerialJavaObject} * to a stream. */ private void writeObject(ObjectOutputStream s) throws IOException { ObjectOutputStream.PutField fields = s.putFields(); fields.put("obj", obj); fields.put("chain", chain); s.writeFields(); } /* * Check to see if there are any Static Fields in this object */ private static boolean hasStaticFields(Field[] fields) { for (Field field : fields) { if ( field.getModifiers() == Modifier.STATIC) { return true; } } return false; } }