001/*
002 * HA-JDBC: High-Availability JDBC
003 * Copyright (C) 2012  Paul Ferraro
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Lesser General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package net.sf.hajdbc.util;
019
020import java.io.ByteArrayInputStream;
021import java.io.ByteArrayOutputStream;
022import java.io.IOException;
023import java.io.ObjectInput;
024import java.io.ObjectInputStream;
025import java.io.ObjectOutput;
026import java.io.ObjectOutputStream;
027import java.security.PrivilegedAction;
028import java.util.Arrays;
029
030/**
031 * Object utility methods.
032 * @author Paul Ferraro
033 */
034public class Objects
035{
036        /**
037         * Compares two objects for equality.
038         * If the objects are arrays, then an array comparison is performed.
039         * @param object1 an object
040         * @param object2 another object
041         * @return true, if the objects are equal, false otherwise
042         */
043        public static boolean equals(Object object1, Object object2)
044        {
045                if ((object1 == null) || (object2 == null)) return object1 == object2;
046
047                if (object1.getClass().isArray() && object2.getClass().isArray())
048                {
049                        if ((object1 instanceof boolean[]) && (object2 instanceof boolean[]))
050                        {
051                                return Arrays.equals((boolean[]) object1, (boolean[]) object2);
052                        }
053                        if ((object1 instanceof byte[]) && (object2 instanceof byte[]))
054                        {
055                                return Arrays.equals((byte[]) object1, (byte[]) object2);
056                        }
057                        if ((object1 instanceof char[]) && (object2 instanceof char[]))
058                        {
059                                return Arrays.equals((char[]) object1, (char[]) object2);
060                        }
061                        if ((object1 instanceof double[]) && (object2 instanceof double[]))
062                        {
063                                return Arrays.equals((double[]) object1, (double[]) object2);
064                        }
065                        if ((object1 instanceof float[]) && (object2 instanceof float[]))
066                        {
067                                return Arrays.equals((float[]) object1, (float[]) object2);
068                        }
069                        if ((object1 instanceof int[]) && (object2 instanceof int[]))
070                        {
071                                return Arrays.equals((int[]) object1, (int[]) object2);
072                        }
073                        if ((object1 instanceof long[]) && (object2 instanceof long[]))
074                        {
075                                return Arrays.equals((long[]) object1, (long[]) object2);
076                        }
077                        if ((object1 instanceof short[]) && (object2 instanceof short[]))
078                        {
079                                return Arrays.equals((short[]) object1, (short[]) object2);
080                        }
081                        return Arrays.equals((Object[]) object1, (Object[]) object2);
082                }
083
084                return object1.equals(object2);
085        }
086        
087        /**
088         * Serializes the specified object.
089         * @param object the serialization target
090         * @return a serialized object
091         */
092        public static byte[] serialize(Object object)
093        {
094                if (object == null) return null;
095                
096                ByteArrayOutputStream bytes = new ByteArrayOutputStream();
097                
098                try
099                {
100                        ObjectOutput output = new ObjectOutputStream(bytes);
101                        try
102                        {
103                                output.writeObject(object);
104                                output.flush();
105                                
106                                return bytes.toByteArray();
107                        }
108                        finally
109                        {
110                                Resources.close(output);
111                        }
112                }
113                catch (IOException e)
114                {
115                        throw new IllegalStateException(e);
116                }
117        }
118        
119        /**
120         * Deserializes the specified bytes into the object of the specified type using HA-JDBC's classloader.
121         * @param bytes a serialized object
122         * @return a deserialized object
123         */
124        public static <T> T deserialize(byte[] bytes)
125        {
126                return deserialize(bytes, Objects.class.getClassLoader());
127        }
128        
129        /**
130         * Deserializes the specified bytes into the object of the specified type.
131         * @param bytes a serialized object
132         * @param loader the classloader used during deserialization
133         * @return a deserialized object
134         */
135        public static <T> T deserialize(byte[] bytes, ClassLoader loader)
136        {
137                if (bytes == null) return null;
138                
139                try
140                {
141                        ObjectInput input = new ObjectInputStream(new ByteArrayInputStream(bytes));
142                        try
143                        {
144                                return readObject(input, loader);
145                        }
146                        finally
147                        {
148                                Resources.close(input);
149                        }
150                }
151                catch (IOException e)
152                {
153                        throw new IllegalStateException(e);
154                }
155        }
156        
157        /**
158         * Reads an object from the input stream using HA-JDBC's classloader.
159         * @param input an input stream
160         * @return a deserialized object
161         */
162        public static <T> T readObject(ObjectInput input)
163        {
164                return readObject(input, Objects.class.getClassLoader());
165        }
166        
167        /**
168         * Reads an object from the input stream using the specified classloader.
169         * @param input an input stream
170         * @param loader a classloader
171         * @return a deserialized object
172         */
173        public static <T> T readObject(ObjectInput input, ClassLoader loader)
174        {
175                ClassLoader originalLoader = getThreadContextClassLoader();
176                
177                setThreadContextClassLoader(loader);
178                
179                try
180                {
181                        return (T) input.readObject();
182                }
183                catch (ClassNotFoundException e)
184                {
185                        throw new IllegalStateException(e);
186                }
187                catch (IOException e)
188                {
189                        throw new IllegalStateException(e);
190                }
191                finally
192                {
193                        setThreadContextClassLoader(originalLoader);
194                }
195        }
196        
197        private Objects()
198        {
199                // Hide
200        }
201        
202        private static ClassLoader getThreadContextClassLoader()
203        {
204                PrivilegedAction<ClassLoader> action = new PrivilegedAction<ClassLoader>()
205                {
206                        @Override
207                        public ClassLoader run()
208                        {
209                                return Thread.currentThread().getContextClassLoader();
210                        }
211                };
212                
213                return Security.run(action);
214        }
215        
216        private static void setThreadContextClassLoader(final ClassLoader loader)
217        {
218                PrivilegedAction<Void> action = new PrivilegedAction<Void>()
219                {
220                        @Override
221                        public Void run()
222                        {
223                                Thread.currentThread().setContextClassLoader(loader);
224                                return null;
225                        }
226                };
227                
228                Security.run(action);
229        }
230}