1/*
2 * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.sql.rowset.serial;
27
28import java.io.*;
29import java.lang.reflect.*;
30import java.util.Arrays;
31import java.util.Vector;
32import javax.sql.rowset.RowSetWarning;
33import jdk.internal.reflect.CallerSensitive;
34import jdk.internal.reflect.Reflection;
35import sun.reflect.misc.ReflectUtil;
36
37/**
38 * A serializable mapping in the Java programming language of an SQL
39 * <code>JAVA_OBJECT</code> value. Assuming the Java object
40 * implements the <code>Serializable</code> interface, this class simply wraps the
41 * serialization process.
42 * <P>
43 * If however, the serialization is not possible because
44 * the Java object is not immediately serializable, this class will
45 * attempt to serialize all non-static members to permit the object
46 * state to be serialized.
47 * Static or transient fields cannot be serialized; an attempt to serialize
48 * them will result in a <code>SerialException</code> object being thrown.
49 *
50 * <h3> Thread safety </h3>
51 *
52 * A SerialJavaObject is not safe for use by multiple concurrent threads.  If a
53 * SerialJavaObject is to be used by more than one thread then access to the
54 * SerialJavaObject should be controlled by appropriate synchronization.
55 *
56 * @author Jonathan Bruce
57 * @since 1.5
58 */
59public class SerialJavaObject implements Serializable, Cloneable {
60
61    /**
62     * Placeholder for object to be serialized.
63     */
64    private Object obj;
65
66
67   /**
68    * Placeholder for all fields in the <code>JavaObject</code> being serialized.
69    */
70    private transient Field[] fields;
71
72    /**
73     * Constructor for <code>SerialJavaObject</code> helper class.
74     *
75     * @param obj the Java <code>Object</code> to be serialized
76     * @throws SerialException if the object is found not to be serializable
77     */
78    public SerialJavaObject(Object obj) throws SerialException {
79
80        // if any static fields are found, an exception
81        // should be thrown
82
83
84        // get Class. Object instance should always be available
85        Class<?> c = obj.getClass();
86
87        // determine if object implements Serializable i/f
88        if (!(obj instanceof java.io.Serializable)) {
89            setWarning(new RowSetWarning("Warning, the object passed to the constructor does not implement Serializable"));
90        }
91
92        // can only determine public fields (obviously). If
93        // any of these are static, this should invalidate
94        // the action of attempting to persist these fields
95        // in a serialized form
96        fields = c.getFields();
97
98        if (hasStaticFields(fields)) {
99            throw new SerialException("Located static fields in " +
100                "object instance. Cannot serialize");
101        }
102
103        this.obj = obj;
104    }
105
106    /**
107     * Returns an <code>Object</code> that is a copy of this <code>SerialJavaObject</code>
108     * object.
109     *
110     * @return a copy of this <code>SerialJavaObject</code> object as an
111     *         <code>Object</code> in the Java programming language
112     * @throws SerialException if the instance is corrupt
113     */
114    public Object getObject() throws SerialException {
115        return this.obj;
116    }
117
118    /**
119     * Returns an array of <code>Field</code> objects that contains each
120     * field of the object that this helper class is serializing.
121     *
122     * @return an array of <code>Field</code> objects
123     * @throws SerialException if an error is encountered accessing
124     * the serialized object
125     * @throws  SecurityException  If a security manager, <i>s</i>, is present
126     * and the caller's class loader is not the same as or an
127     * ancestor of the class loader for the class of the
128     * {@linkplain #getObject object} being serialized
129     * and invocation of {@link SecurityManager#checkPackageAccess
130     * s.checkPackageAccess()} denies access to the package
131     * of that class.
132     * @see Class#getFields
133     */
134    @CallerSensitive
135    public Field[] getFields() throws SerialException {
136        if (fields != null) {
137            Class<?> c = this.obj.getClass();
138            SecurityManager sm = System.getSecurityManager();
139            if (sm != null) {
140                /*
141                 * Check if the caller is allowed to access the specified class's package.
142                 * If access is denied, throw a SecurityException.
143                 */
144                Class<?> caller = Reflection.getCallerClass();
145                if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),
146                                                        c.getClassLoader())) {
147                    ReflectUtil.checkPackageAccess(c);
148                }
149            }
150            return c.getFields();
151        } else {
152            throw new SerialException("SerialJavaObject does not contain" +
153                " a serialized object instance");
154        }
155    }
156
157    /**
158     * The identifier that assists in the serialization of this
159     * <code>SerialJavaObject</code> object.
160     */
161    static final long serialVersionUID = -1465795139032831023L;
162
163    /**
164     * A container for the warnings issued on this <code>SerialJavaObject</code>
165     * object. When there are multiple warnings, each warning is chained to the
166     * previous warning.
167     */
168    Vector<RowSetWarning> chain;
169
170    /**
171     * Compares this SerialJavaObject to the specified object.
172     * The result is {@code true} if and only if the argument
173     * is not {@code null} and is a {@code SerialJavaObject}
174     * object that is identical to this object
175     *
176     * @param  o The object to compare this {@code SerialJavaObject} against
177     *
178     * @return  {@code true} if the given object represents a {@code SerialJavaObject}
179     *          equivalent to this SerialJavaObject, {@code false} otherwise
180     *
181     */
182    public boolean equals(Object o) {
183        if (this == o) {
184            return true;
185        }
186        if (o instanceof SerialJavaObject) {
187            SerialJavaObject sjo = (SerialJavaObject) o;
188            return obj.equals(sjo.obj);
189        }
190        return false;
191    }
192
193    /**
194     * Returns a hash code for this SerialJavaObject. The hash code for a
195     * {@code SerialJavaObject} object is taken as the hash code of
196     * the {@code Object} it stores
197     *
198     * @return  a hash code value for this object.
199     */
200    public int hashCode() {
201        return 31 + obj.hashCode();
202    }
203
204    /**
205     * Returns a clone of this {@code SerialJavaObject}.
206     *
207     * @return  a clone of this SerialJavaObject
208     */
209
210    public Object clone() {
211        try {
212            SerialJavaObject sjo = (SerialJavaObject) super.clone();
213            sjo.fields = Arrays.copyOf(fields, fields.length);
214            if (chain != null)
215                sjo.chain = new Vector<>(chain);
216            return sjo;
217        } catch (CloneNotSupportedException ex) {
218            // this shouldn't happen, since we are Cloneable
219            throw new InternalError();
220        }
221    }
222
223    /**
224     * Registers the given warning.
225     */
226    private void setWarning(RowSetWarning e) {
227        if (chain == null) {
228            chain = new Vector<>();
229        }
230        chain.add(e);
231    }
232
233    /**
234     * readObject is called to restore the state of the {@code SerialJavaObject}
235     * from a stream.
236     */
237    private void readObject(ObjectInputStream s)
238            throws IOException, ClassNotFoundException {
239
240        ObjectInputStream.GetField fields1 = s.readFields();
241        @SuppressWarnings("unchecked")
242        Vector<RowSetWarning> tmp = (Vector<RowSetWarning>)fields1.get("chain", null);
243        if (tmp != null)
244            chain = new Vector<>(tmp);
245
246        obj = fields1.get("obj", null);
247        if (obj != null) {
248            fields = obj.getClass().getFields();
249            if(hasStaticFields(fields))
250                throw new IOException("Located static fields in " +
251                "object instance. Cannot serialize");
252        } else {
253            throw new IOException("Object cannot be null!");
254        }
255
256    }
257
258    /**
259     * writeObject is called to save the state of the {@code SerialJavaObject}
260     * to a stream.
261     */
262    private void writeObject(ObjectOutputStream s)
263            throws IOException {
264        ObjectOutputStream.PutField fields = s.putFields();
265        fields.put("obj", obj);
266        fields.put("chain", chain);
267        s.writeFields();
268    }
269
270    /*
271     * Check to see if there are any Static Fields in this object
272     */
273    private static boolean hasStaticFields(Field[] fields) {
274        for (Field field : fields) {
275            if ( field.getModifiers() == Modifier.STATIC) {
276                return true;
277            }
278        }
279        return false;
280    }
281}
282