http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b08492a5/modules/core/src/main/java/org/apache/ignite/marshaller/MarshallerExclusions.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/marshaller/MarshallerExclusions.java index 25d2ccd,0000000..5ea14c2 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/marshaller/MarshallerExclusions.java +++ b/modules/core/src/main/java/org/apache/ignite/marshaller/MarshallerExclusions.java @@@ -1,153 -1,0 +1,153 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.marshaller; + +import org.apache.ignite.*; +import org.apache.ignite.compute.*; +import org.apache.ignite.internal.*; +import org.apache.ignite.internal.executor.*; +import org.apache.ignite.internal.util.*; +import org.apache.ignite.internal.util.typedef.internal.*; + +import javax.management.*; +import java.util.*; +import java.util.concurrent.*; + +/** + * Controls what classes should be excluded from marshalling by default. + */ +public final class MarshallerExclusions { + /** + * Classes that must be included in serialization. All marshallers must + * included these classes. + * <p> + * Note that this list supercedes {@link #EXCL_CLASSES}. + */ + private static final Class<?>[] INCL_CLASSES = new Class[] { - // GridGain classes. ++ // Ignite classes. + GridLoggerProxy.class, + GridExecutorService.class + }; + + /** */ + private static final Map<Class<?>, Boolean> cache = new GridBoundedConcurrentLinkedHashMap<>( + 512, 512, 0.75f, 16); + + /** + * Excluded grid classes from serialization. All marshallers must omit + * these classes. Fields of these types should be serialized as {@code null}. + * <p> + * Note that {@link #INCL_CLASSES} supercedes this list. + */ + private static final Class<?>[] EXCL_CLASSES; + + /** + * + */ + static { + Class springCtxCls = null; + + try { + springCtxCls = Class.forName("org.springframework.context.ApplicationContext"); + } + catch (Exception ignored) { + // No-op. + } + + List<Class<?>> excl = new ArrayList<>(); + - // Non-GridGain classes. ++ // Non-Ignite classes. + excl.add(MBeanServer.class); + excl.add(ExecutorService.class); + excl.add(ClassLoader.class); + excl.add(Thread.class); + + if (springCtxCls != null) + excl.add(springCtxCls); + - // GridGain classes. ++ // Ignite classes. + excl.add(IgniteLogger.class); + excl.add(ComputeTaskSession.class); + excl.add(ComputeLoadBalancer.class); + excl.add(ComputeJobContext.class); + excl.add(Marshaller.class); + excl.add(GridComponent.class); + excl.add(ComputeTaskContinuousMapper.class); + + EXCL_CLASSES = U.toArray(excl, new Class[excl.size()]); + } + + /** + * Ensures singleton. + */ + private MarshallerExclusions() { + // No-op. + } + + /** + * Checks given class against predefined set of excluded types. + * + * @param cls Class to check. + * @return {@code true} if class should be excluded, {@code false} otherwise. + */ + @SuppressWarnings("ForLoopReplaceableByForEach") + private static boolean isExcluded0(Class<?> cls) { + assert cls != null; + + final Class<?>[] inc = INCL_CLASSES; + + // NOTE: don't use foreach for performance reasons. + for (int i = 0; i < inc.length; i++) + if (inc[i].isAssignableFrom(cls)) + return false; + + final Class<?>[] exc = EXCL_CLASSES; + + // NOTE: don't use foreach for performance reasons. + for (int i = 0; i < exc.length; i++) + if (exc[i].isAssignableFrom(cls)) + return true; + + return false; + } + + /** + * Checks whether or not given class should be excluded from marshalling. + * + * @param cls Class to check. + * @return {@code true} if class should be excluded, {@code false} otherwise. + */ + public static boolean isExcluded(Class<?> cls) { + Boolean res = cache.get(cls); + + if (res == null) { + res = isExcluded0(cls); + + cache.put(cls, res); + } + + return res; + } + + /** + * Intended for test purposes only. + */ + public static void clearCache() { + cache.clear(); + } +}
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b08492a5/modules/core/src/main/java/org/apache/ignite/marshaller/jdk/JdkMarshaller.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/marshaller/jdk/JdkMarshaller.java index 868abaf,0000000..f03a4f5 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/marshaller/jdk/JdkMarshaller.java +++ b/modules/core/src/main/java/org/apache/ignite/marshaller/jdk/JdkMarshaller.java @@@ -1,117 -1,0 +1,117 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.marshaller.jdk; + +import org.apache.ignite.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.marshaller.*; +import org.jetbrains.annotations.*; + +import java.io.*; + +/** + * Implementation of {@link org.apache.ignite.marshaller.Marshaller} based on JDK serialization mechanism. + * <p> + * <h1 class="header">Configuration</h1> + * <h2 class="header">Mandatory</h2> + * This marshaller has no mandatory configuration parameters. + * <h2 class="header">Java Example</h2> + * {@code GridJdkMarshaller} needs to be explicitly configured to override default {@link org.apache.ignite.marshaller.optimized.OptimizedMarshaller}. + * <pre name="code" class="java"> + * GridJdkMarshaller marshaller = new GridJdkMarshaller(); + * + * GridConfiguration cfg = new GridConfiguration(); + * + * // Override default marshaller. + * cfg.setMarshaller(marshaller); + * + * // Starts grid. + * G.start(cfg); + * </pre> + * <h2 class="header">Spring Example</h2> + * GridJdkMarshaller can be configured from Spring XML configuration file: + * <pre name="code" class="xml"> - * <bean id="grid.custom.cfg" class="org.gridgain.grid.GridConfiguration" singleton="true"> ++ * <bean id="grid.custom.cfg" class="org.apache.ignite.configuration.IgniteConfiguration" singleton="true"> + * ... + * <property name="marshaller"> + * <bean class="org.apache.ignite.marshaller.jdk.GridJdkMarshaller"/> + * </property> + * ... + * </bean> + * </pre> + * <p> + * <img src="http://www.gridgain.com/images/spring-small.png"> + * <br> + * For information about Spring framework visit <a href="http://www.springframework.org/">www.springframework.org</a> + */ +public class JdkMarshaller extends AbstractMarshaller { + /** {@inheritDoc} */ + @Override public void marshal(@Nullable Object obj, OutputStream out) throws IgniteCheckedException { + assert out != null; + + ObjectOutputStream objOut = null; + + try { + objOut = new JdkMarshallerObjectOutputStream(new JdkMarshallerOutputStreamWrapper(out)); + + // Make sure that we serialize only task, without class loader. + objOut.writeObject(obj); + + objOut.flush(); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to serialize object: " + obj, e); + } + finally{ + U.closeQuiet(objOut); + } + } + + /** {@inheritDoc} */ + @SuppressWarnings({"unchecked"}) + @Override public <T> T unmarshal(InputStream in, @Nullable ClassLoader clsLdr) throws IgniteCheckedException { + assert in != null; + + if (clsLdr == null) + clsLdr = getClass().getClassLoader(); + + ObjectInputStream objIn = null; + + try { + objIn = new JdkMarshallerObjectInputStream(new JdkMarshallerInputStreamWrapper(in), clsLdr); + + return (T)objIn.readObject(); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to deserialize object with given class loader: " + clsLdr, e); + } + catch (ClassNotFoundException e) { + throw new IgniteCheckedException("Failed to find class with given class loader for unmarshalling " + + "(make sure same versions of all classes are available on all nodes or enable peer-class-loading): " + + clsLdr, e); + } + finally{ + U.closeQuiet(objIn); + } + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdkMarshaller.class, this); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b08492a5/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassResolver.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassResolver.java index f793857,0000000..9a7c981 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassResolver.java +++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassResolver.java @@@ -1,477 -1,0 +1,477 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.marshaller.optimized; + +import org.apache.ignite.internal.util.*; +import org.apache.ignite.internal.util.typedef.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.lang.*; +import org.jdk8.backport.*; +import org.jetbrains.annotations.*; + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.Date; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.concurrent.locks.*; + +/** + * Resolves class names by serialVersionUID. + */ +@SuppressWarnings({"UnnecessaryFullyQualifiedName", "unchecked"}) +class OptimizedClassResolver { + /** File name to generate. */ + private static final String FILE_NAME = "optimized-classnames.properties"; + + /** */ + private static final Map<String, Integer> ggxName2id = new HashMap<>(); + + /** */ + private static final T2<Class<?>, OptimizedClassDescriptor>[] ggxId2name; + + /** */ + private static final Map<String, Integer> ggName2id = new HashMap<>(); + + /** */ + private static final T3<String, Class<?>, OptimizedClassDescriptor>[] ggId2name; + + /** */ + private static Map<String, Integer> usrName2Id; + + /** */ + private static T3<String, Class<?>, OptimizedClassDescriptor>[] usrId2Name; + + /** */ + private static final int HEADER_NAME = 255; + + /** */ + private static final int HEADER_GG_NAME = 254; + + /** */ + private static final int HEADER_USER_NAME = 253; + + /** */ + private static final int HEADER_ARRAY = 252; + + /** + * Initialize predefined classes to optimize. + */ + static { + Class[] superOptCls = new Class[] { + // Array types. + byte[].class, + short[].class, + int[].class, + long[].class, + float[].class, + double[].class, + boolean[].class, + char[].class, + + // Boxed types. + Byte.class, + Short.class, + Integer.class, + Long.class, + Float.class, + Double.class, + Boolean.class, + Character.class, + String.class, + + // Atomic. + AtomicBoolean.class,AtomicInteger.class, + AtomicLong.class,AtomicReference.class, + AtomicMarkableReference.class, + AtomicStampedReference.class, + AtomicIntegerArray.class, + AtomicReferenceArray.class, + + // Concurrent types. + ConcurrentHashMap.class, + ConcurrentLinkedQueue.class, + ConcurrentSkipListMap.class, + ConcurrentSkipListSet.class, + LinkedBlockingDeque.class, + LinkedBlockingQueue.class, + PriorityBlockingQueue.class, + CopyOnWriteArrayList.class, + CopyOnWriteArraySet.class, + + // Locks. + ReentrantLock.class, + ReentrantReadWriteLock.class, + ReentrantReadWriteLock.ReadLock.class, + ReentrantReadWriteLock.WriteLock.class, + + // Util types. + Date.class, + UUID.class, + Calendar.class, + Random.class, + Calendar.class, + Currency.class, + ArrayList.class, + LinkedList.class, + Stack.class, + Vector.class, + HashMap.class, + HashSet.class, + Hashtable.class, + TreeMap.class, + TreeSet.class, + IdentityHashMap.class, + LinkedHashMap.class, + LinkedHashSet.class, + ArrayDeque.class, + BitSet.class, + EnumMap.class, + EnumSet.class, + + // SQL types. + java.sql.Date.class, + Time.class, + Timestamp.class, + + // Math types. + BigDecimal.class, + BigInteger.class, + - // GridGain types. ++ // Ignite types. + IgniteUuid.class, + GridBoundedConcurrentOrderedSet.class, + GridBoundedLinkedHashSet.class, + GridConcurrentHashSet.class, + ConcurrentLinkedDeque8.class, + GridConcurrentPhantomHashSet.class, + GridConcurrentSkipListSet.class, + GridConcurrentWeakHashSet.class, + GridIdentityHashSet.class, + GridLeanSet.class, + GridSetWrapper.class + }; + + // Have to leave a range for special purposes. + assert superOptCls.length < 230; + + ggxId2name = new T2[superOptCls.length]; + + for (int i = 0; i < superOptCls.length; i++) { + Class cls = superOptCls[i]; + + ggxName2id.put(cls.getName(), i); + ggxId2name[i] = new T2<Class<?>, OptimizedClassDescriptor>(cls, null); + } + + BufferedReader reader = new BufferedReader(new InputStreamReader( + OptimizedClassResolver.class.getResourceAsStream(FILE_NAME), + OptimizedMarshallerUtils.UTF_8)); + + List<T3<String, Class<?>, OptimizedClassDescriptor>> ggId2name0 = + new LinkedList<>(); + + try { + for (int i = 0; ; i++) { + String clsName = reader.readLine(); + + if (clsName == null) + break; + + ggName2id.put(clsName, i); + ggId2name0.add(new T3<String, Class<?>, OptimizedClassDescriptor>(clsName, null, null)); + } + + ggId2name = ggId2name0.toArray(new T3[ggId2name0.size()]); + } + catch (IOException e) { + throw new AssertionError(e); + } + finally { + U.close(reader, null); + } + } + + /** + * Ensure singleton. + */ + private OptimizedClassResolver() { + // No-op. + } + + /** + * @param usrName2id0 From name to ID. + * @param usrId2Name0 From ID to name. + */ + static void userClasses(@Nullable Map<String, Integer> usrName2id0, + @Nullable T3<String, Class<?>, OptimizedClassDescriptor>[] usrId2Name0) { + usrName2Id = usrName2id0; + usrId2Name = usrId2Name0; + } + + /** + * @param in DataInput to read from. + * @param clsLdr ClassLoader. + * @return Class descriptor. + * @throws IOException If serial version UID failed. + * @throws ClassNotFoundException If the class cannot be located by the specified class loader. + */ + static OptimizedClassDescriptor readClass(DataInput in, ClassLoader clsLdr) + throws IOException, ClassNotFoundException { + assert in != null; + assert clsLdr != null; + + int hdr = in.readByte() & 0xff; + + if (hdr < ggxId2name.length) { + T2<Class<?>, OptimizedClassDescriptor> ggxT = ggxId2name[hdr]; + + OptimizedClassDescriptor desc = ggxT.get2(); + + if (desc == null) { + desc = OptimizedMarshallerUtils.classDescriptor(ggxT.get1(), null); + + ggxT.set2(desc); + } + + return desc; + } + + String name; + Class<?> cls; + OptimizedClassDescriptor desc; + + switch (hdr) { + case HEADER_GG_NAME: + int ggId = in.readInt(); + + T3<String, Class<?>, OptimizedClassDescriptor> ggT; + + try { + ggT = ggId2name[ggId]; + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ClassNotFoundException("Failed to find optimized class ID " + + "(is same Ignite version running on all nodes?): " + ggId, e); + } + + name = ggT.get1(); + cls = ggT.get2(); + desc = ggT.get3(); + + if (desc == null) { + if (clsLdr == U.gridClassLoader()) { + if (cls == null) { + cls = forName(name, clsLdr); + + ggT.set2(cls); + } + + desc = OptimizedMarshallerUtils.classDescriptor(cls, null); + + ggT.set3(desc); + } + else { + cls = forName(name, clsLdr); + + desc = OptimizedMarshallerUtils.classDescriptor(cls, null); + } + } + + break; + + case HEADER_USER_NAME: + int usrId = in.readInt(); + + T3<String, Class<?>, OptimizedClassDescriptor> usrT; + + try { + if (usrId2Name != null) + usrT = usrId2Name[usrId]; + else + throw new ClassNotFoundException("Failed to find user defined class ID " + + "(make sure to register identical classes on all nodes for optimization): " + usrId); + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ClassNotFoundException("Failed to find user defined class ID " + + "(make sure to register identical classes on all nodes for optimization): " + usrId, e); + } + + name = usrT.get1(); + cls = usrT.get2(); + desc = usrT.get3(); + + if (desc == null) { + if (cls == null) { + cls = forName(name, clsLdr); + + usrT.set2(cls); + } + + desc = OptimizedMarshallerUtils.classDescriptor(cls, null); + + usrT.set3(desc); + } + + break; + + case HEADER_ARRAY: + name = readClass(in, clsLdr).name(); + + name = name.charAt(0) == '[' ? "[" + name : "[L" + name + ';'; + + cls = forName(name, clsLdr); + + return OptimizedMarshallerUtils.classDescriptor(cls, null); + + case HEADER_NAME: + name = in.readUTF(); + + cls = forName(name, clsLdr); + + desc = OptimizedMarshallerUtils.classDescriptor(cls, null); + + break; + + default: + throw new IOException("Unexpected optimized stream header: " + hdr); + } + + short actual = desc.shortId(); + + short exp = in.readShort(); + + if (actual != exp) + throw new ClassNotFoundException("Optimized stream class checksum mismatch " + + "(is same version of marshalled class present on all nodes?) " + + "[expected=" + exp + ", actual=" + actual + ", cls=" + cls + ']'); + + return desc; + } + + /** + * @param out Output. + * @param desc Class descriptor. + * @throws IOException In case of error. + */ + static void writeClass(DataOutput out, OptimizedClassDescriptor desc) throws IOException { + assert out != null; + assert desc != null; + + int hdr = desc.header(); + + out.writeByte(hdr); + + switch (hdr) { + case HEADER_GG_NAME: + case HEADER_USER_NAME: + out.writeInt(desc.id()); + out.writeShort(desc.shortId()); + + return; + + case HEADER_ARRAY: + writeClass(out, OptimizedMarshallerUtils.classDescriptor(desc.componentType(), null)); + + return; + + case HEADER_NAME: + out.writeUTF(desc.name()); + out.writeShort(desc.shortId()); + } + } + + /** + * @param cls Class to write. + * @return Data for {@code writeClass} method. + */ + static T2<Integer, Integer> writeClassData(Class<?> cls) { + assert cls != null; + + String name = cls.getName(); + + Integer superHdr = ggxName2id.get(name); + + if (superHdr != null) + return new T2<>(superHdr, null); + + Integer id; + + if ((id = ggName2id.get(name)) != null) + return new T2<>(HEADER_GG_NAME, id); + + if (usrName2Id != null && (id = usrName2Id.get(name)) != null) + return new T2<>(HEADER_USER_NAME, id); + + if (cls.isArray()) + return new T2<>(HEADER_ARRAY, null); + + return new T2<>(HEADER_NAME, null); + } + + /** + * @param name Class name. + * @param ldr Class loader. + * @return Class. + * @throws ClassNotFoundException If class not found. + */ + private static Class<?> forName(String name, ClassLoader ldr) throws ClassNotFoundException { + Class<?> cls = primitive(name); + + if (cls == null) + cls = OptimizedMarshallerUtils.forName(name, ldr); + + return cls; + } + + /** + * @param name Name of primitive class. + * @return Primitive type class or null. + */ + @SuppressWarnings("TypeMayBeWeakened") + @Nullable private static Class<?> primitive(String name) { + if (name.length() > 7) + return null; + + switch (name.charAt(0)) { + case 'b': + if ("boolean".equals(name)) + return boolean.class; + + return "byte".equals(name) ? byte.class : null; + case 's': + return "short".equals(name) ? short.class : null; + case 'i': + return "int".equals(name) ? int.class : null; + case 'l': + return "long".equals(name) ? long.class : null; + case 'c': + return "char".equals(name) ? char.class : null; + case 'f': + return "float".equals(name) ? float.class : null; + case 'd': + return "double".equals(name) ? double.class : null; + case 'v': + return "void".equals(name) ? void.class : null; + } + + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b08492a5/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallable.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallable.java index a4a8d36,0000000..729b062 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallable.java +++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallable.java @@@ -1,65 -1,0 +1,65 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.marshaller.optimized; + +import java.util.*; + +/** + * Optional interface which helps make serialization even faster by removing internal + * look-ups for classes. + * <p> + * All implementation must have the following: + * <ul> + * <li> + * Must have static filed (private or public) declared of type {@link Object} - * with name {@code GG_CLASS_ID}. GridGain will reflectively initialize this field with ++ * with name {@code GG_CLASS_ID}. Ignite will reflectively initialize this field with + * proper class ID during system startup. + * </li> + * <li> + * Must return the value of {@code GG_CLASS_ID} field from {@link #ggClassId} method. + * </li> + * </ul> + * Here is a sample implementation: + * <pre name="code" class="java"> + * // For better performance consider implementing java.io.Externalizable interface. + * class ExampleMarshallable implements GridOptimizedMarshallable, Serializable { + * // Class ID field required by 'GridOptimizedMarshallable'. + * private static Object GG_CLASS_ID; + * + * ... + * + * @ public Object ggClassId() { + * return GG_CLASS_ID; + * } + * } + * </pre> + * <p> + * Note that for better performance you should also specify list of classes you + * plan to serialize via {@link OptimizedMarshaller#setClassNames(List)} method. + */ +public interface OptimizedMarshallable { + /** */ + public static final String CLS_ID_FIELD_NAME = "GG_CLASS_ID"; + + /** + * Implementation of this method should simply return value of {@code GG_CLASS_ID} field. + * + * @return Class ID for optimized marshalling. + */ + public Object ggClassId(); +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b08492a5/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java index 7f8a3f0,0000000..57d0506 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java +++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshaller.java @@@ -1,393 -1,0 +1,393 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.marshaller.optimized; + +import org.apache.ignite.*; +import org.apache.ignite.internal.util.*; +import org.apache.ignite.internal.util.typedef.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.marshaller.*; +import org.jetbrains.annotations.*; +import sun.misc.*; + +import java.io.*; +import java.net.*; +import java.util.*; + +/** + * Optimized implementation of {@link org.apache.ignite.marshaller.Marshaller}. Unlike {@link org.apache.ignite.marshaller.jdk.JdkMarshaller}, + * which is based on standard {@link ObjectOutputStream}, this marshaller does not + * enforce that all serialized objects implement {@link Serializable} interface. It is also + * about 20 times faster as it removes lots of serialization overhead that exists in + * default JDK implementation. + * <p> + * {@code GridOptimizedMarshaller} is tested only on Java HotSpot VM on other VMs + * it could yield unexpected results. It is the default marshaller on Java HotSpot VMs + * and will be used if no other marshaller was explicitly configured. + * <p> + * <h1 class="header">Configuration</h1> + * <h2 class="header">Mandatory</h2> + * This marshaller has no mandatory configuration parameters. + * <h2 class="header">Java Example</h2> + * <pre name="code" class="java"> + * GridOptimizedMarshaller marshaller = new GridOptimizedMarshaller(); + * + * // Enforce Serializable interface. + * marshaller.setRequireSerializable(true); + * + * GridConfiguration cfg = new GridConfiguration(); + * + * // Override marshaller. + * cfg.setMarshaller(marshaller); + * + * // Starts grid. + * G.start(cfg); + * </pre> + * <h2 class="header">Spring Example</h2> + * GridOptimizedMarshaller can be configured from Spring XML configuration file: + * <pre name="code" class="xml"> - * <bean id="grid.custom.cfg" class="org.gridgain.grid.GridConfiguration" singleton="true"> ++ * <bean id="grid.custom.cfg" class="org.apache.ignite.configuration.IgniteConfiguration" singleton="true"> + * ... + * <property name="marshaller"> + * <bean class="org.apache.ignite.marshaller.optimized.GridOptimizedMarshaller"> + * <property name="requireSerializable">true</property> + * </bean> + * </property> + * ... + * </bean> + * </pre> + * <p> + * <img src="http://www.gridgain.com/images/spring-small.png"> + * <br> + * For information about Spring framework visit <a href="http://www.springframework.org/">www.springframework.org</a> + */ +public class OptimizedMarshaller extends AbstractMarshaller { + /** Whether or not to require an object to be serializable in order to be marshalled. */ + private boolean requireSer = true; + + /** Default class loader. */ + private final ClassLoader dfltClsLdr = getClass().getClassLoader(); + + /** + * Initializes marshaller not to enforce {@link Serializable} interface. + * + * @throws IgniteException If this marshaller is not supported on the current JVM. + */ + public OptimizedMarshaller() { + if (!available()) + throw new IgniteException("Using GridOptimizedMarshaller on unsupported JVM version (some of " + + "JVM-private APIs required for the marshaller to work are missing)."); + } + + /** + * Initializes marshaller with given serialization flag. If {@code true}, + * then objects will be required to implement {@link Serializable} in order + * to be serialize. + * + * @param requireSer Flag to enforce {@link Serializable} interface or not. If {@code true}, + * then objects will be required to implement {@link Serializable} in order to be + * marshalled, if {@code false}, then such requirement will be relaxed. + * @throws IgniteException If this marshaller is not supported on the current JVM. + */ + public OptimizedMarshaller(boolean requireSer) { + this(); + + this.requireSer = requireSer; + } + + /** + * Initializes marshaller with given serialization flag. If {@code true}, + * then objects will be required to implement {@link Serializable} in order + * to be serialize. + * + * @param requireSer Flag to enforce {@link Serializable} interface or not. If {@code true}, + * then objects will be required to implement {@link Serializable} in order to be + * marshalled, if {@code false}, then such requirement will be relaxed. + * @param clsNames User preregistered class names. + * @param clsNamesPath Path to a file with user preregistered class names. + * @param poolSize Object streams pool size. + * @throws IgniteCheckedException If an I/O error occurs while writing stream header. + * @throws IgniteException If this marshaller is not supported on the current JVM. + */ + public OptimizedMarshaller(boolean requireSer, @Nullable List<String> clsNames, + @Nullable String clsNamesPath, int poolSize) throws IgniteCheckedException { + this(requireSer); + + setClassNames(clsNames); + setClassNamesPath(clsNamesPath); + setPoolSize(poolSize); + } + + /** + * Adds provided class names for marshalling optimization. + * <p> + * <b>NOTE</b>: these collections of classes must be identical on all nodes and in the same order. + * + * @param clsNames User preregistered class names to add. + */ + @SuppressWarnings("unchecked") + public void setClassNames(@Nullable List<String> clsNames) { + if (clsNames != null && !clsNames.isEmpty()) { + String[] clsNamesArr = clsNames.toArray(new String[clsNames.size()]); + + Arrays.sort(clsNamesArr); + + Map<String, Integer> name2id = U.newHashMap(clsNamesArr.length); + T3<String, Class<?>, OptimizedClassDescriptor>[] id2name = new T3[clsNamesArr.length]; + + int i = 0; + + for (String name : clsNamesArr) { + name2id.put(name, i); + id2name[i++] = new T3<>(name, null, null); + } + + OptimizedClassResolver.userClasses(name2id, id2name); + } + } + + /** + * Specifies a name of the file which lists all class names to be optimized. + * The file path can either be absolute path, relative to {@code IGNITE_HOME}, + * or specify a resource file on the class path. + * <p> + * The format of the file is class name per line, like this: + * <pre> + * ... + * com.example.Class1 + * com.example.Class2 + * ... + * </pre> + * <p> + * <b>NOTE</b>: this class list must be identical on all nodes and in the same order. + * + * @param path Path to a file with user preregistered class names. + * @throws IgniteCheckedException If an error occurs while writing stream header. + */ + public void setClassNamesPath(@Nullable String path) throws IgniteCheckedException { + if (path == null) + return; + - URL url = IgniteUtils.resolveGridGainUrl(path, false); ++ URL url = IgniteUtils.resolveIgniteUrl(path, false); + + if (url == null) + throw new IgniteCheckedException("Failed to find resource for name: " + path); + + List<String> clsNames; + + try { + clsNames = new LinkedList<>(); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), OptimizedMarshallerUtils.UTF_8))) { + String clsName; + + while ((clsName = reader.readLine()) != null) + clsNames.add(clsName); + } + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to read class names from path: " + path, e); + } + + setClassNames(clsNames); + } + + /** + * Specifies size of cached object streams used by marshaller. Object streams are cached for + * performance reason to avoid costly recreation for every serialization routine. If {@code 0} (default), + * pool is not used and each thread has its own cached object stream which it keeps reusing. + * <p> + * Since each stream has an internal buffer, creating a stream for each thread can lead to + * high memory consumption if many large messages are marshalled or unmarshalled concurrently. + * Consider using pool in this case. This will limit number of streams that can be created and, + * therefore, decrease memory consumption. + * <p> + * NOTE: Using streams pool can decrease performance since streams will be shared between + * different threads which will lead to more frequent context switching. + * + * @param poolSize Streams pool size. If {@code 0}, pool is not used. + */ + public void setPoolSize(int poolSize) { + OptimizedObjectStreamRegistry.poolSize(poolSize); + } + + /** + * @return Whether to enforce {@link Serializable} interface. + */ + public boolean isRequireSerializable() { + return requireSer; + } + + /** + * Sets flag to enforce {@link Serializable} interface or not. + * + * @param requireSer Flag to enforce {@link Serializable} interface or not. If {@code true}, + * then objects will be required to implement {@link Serializable} in order to be + * marshalled, if {@code false}, then such requirement will be relaxed. + */ + public void setRequireSerializable(boolean requireSer) { + this.requireSer = requireSer; + } + + /** {@inheritDoc} */ + @Override public void marshal(@Nullable Object obj, OutputStream out) throws IgniteCheckedException { + assert out != null; + + OptimizedObjectOutputStream objOut = null; + + try { + objOut = OptimizedObjectStreamRegistry.out(); + + objOut.requireSerializable(requireSer); + + objOut.out().outputStream(out); + + objOut.writeObject(obj); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to serialize object: " + obj, e); + } + finally { + OptimizedObjectStreamRegistry.closeOut(objOut); + } + } + + /** {@inheritDoc} */ + @Override public byte[] marshal(@Nullable Object obj) throws IgniteCheckedException { + OptimizedObjectOutputStream objOut = null; + + try { + objOut = OptimizedObjectStreamRegistry.out(); + + objOut.requireSerializable(requireSer); + + objOut.writeObject(obj); + + return objOut.out().array(); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to serialize object: " + obj, e); + } + finally { + OptimizedObjectStreamRegistry.closeOut(objOut); + } + } + + /** {@inheritDoc} */ + @Override public <T> T unmarshal(InputStream in, @Nullable ClassLoader clsLdr) throws IgniteCheckedException { + assert in != null; + + OptimizedObjectInputStream objIn = null; + + try { + objIn = OptimizedObjectStreamRegistry.in(); + + objIn.classLoader(clsLdr != null ? clsLdr : dfltClsLdr); + + objIn.in().inputStream(in); + + return (T)objIn.readObject(); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to deserialize object with given class loader: " + clsLdr, e); + } + catch (ClassNotFoundException e) { + throw new IgniteCheckedException("Failed to find class with given class loader for unmarshalling " + + "(make sure same versions of all classes are available on all nodes or enable peer-class-loading): " + + clsLdr, e); + } + finally { + OptimizedObjectStreamRegistry.closeIn(objIn); + } + } + + /** {@inheritDoc} */ + @Override public <T> T unmarshal(byte[] arr, @Nullable ClassLoader clsLdr) throws IgniteCheckedException { + assert arr != null; + + OptimizedObjectInputStream objIn = null; + + try { + objIn = OptimizedObjectStreamRegistry.in(); + + objIn.classLoader(clsLdr != null ? clsLdr : dfltClsLdr); + + objIn.in().bytes(arr, arr.length); + + return (T)objIn.readObject(); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to deserialize object with given class loader: " + clsLdr, e); + } + catch (ClassNotFoundException e) { + throw new IgniteCheckedException("Failed to find class with given class loader for unmarshalling " + + "(make sure same version of all classes are available on all nodes or enable peer-class-loading): " + + clsLdr, e); + } + finally { + OptimizedObjectStreamRegistry.closeIn(objIn); + } + } + + /** + * Checks whether {@code GridOptimizedMarshaller} is able to work on the current JVM. + * <p> + * As long as {@code GridOptimizedMarshaller} uses JVM-private API, which is not guaranteed + * to be available on all JVM, this method should be called to ensure marshaller could work properly. + * <p> + * Result of this method is automatically checked in constructor. + * + * @return {@code true} if {@code GridOptimizedMarshaller} can work on the current JVM or + * {@code false} if it can't. + */ + @SuppressWarnings({"TypeParameterExtendsFinalClass", "ErrorNotRethrown"}) + public static boolean available() { + try { + Unsafe unsafe = GridUnsafe.unsafe(); + + Class<? extends Unsafe> unsafeCls = unsafe.getClass(); + + unsafeCls.getMethod("allocateInstance", Class.class); + unsafeCls.getMethod("copyMemory", Object.class, long.class, Object.class, long.class, long.class); + + return true; + } + catch (Exception ignored) { + return false; + } + catch (NoClassDefFoundError ignored) { + return false; + } + } + + /** + * Undeployment callback invoked when class loader is being undeployed. + * + * @param ldr Class loader being undeployed. + */ + public static void onUndeploy(ClassLoader ldr) { + OptimizedMarshallerUtils.onUndeploy(ldr); + } + + /** + * Clears internal caches and frees memory. Usually called on system stop. + */ + public static void clearCache() { + OptimizedMarshallerUtils.clearCache(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b08492a5/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java index b6b3774,0000000..ef589e9 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java +++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java @@@ -1,1018 -1,0 +1,1018 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.marshaller.optimized; + +import org.apache.ignite.internal.util.*; +import org.apache.ignite.internal.util.io.*; +import org.apache.ignite.internal.util.typedef.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.lang.*; +import sun.misc.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import static org.apache.ignite.marshaller.optimized.OptimizedMarshallerUtils.*; + +/** + * Optimized object input stream. + */ +class OptimizedObjectInputStream extends ObjectInputStream { + /** Unsafe. */ + private static final Unsafe UNSAFE = GridUnsafe.unsafe(); + + /** Dummy object for HashSet. */ + private static final Object DUMMY = new Object(); + + /** */ + private final HandleTable handles = new HandleTable(10); + + /** */ + private ClassLoader clsLdr; + + /** */ + private GridDataInput in; + + /** */ + private Object curObj; + + /** */ + private List<T2<OptimizedFieldType, Long>> curFields; + + /** */ + private List<IgniteBiTuple<Integer, OptimizedFieldType>> curFieldInfoList; + + /** */ + private Map<String, IgniteBiTuple<Integer, OptimizedFieldType>> curFieldInfoMap; + + /** */ + private Class<?> curCls; + + /** + * @param in Input. + * @throws IOException In case of error. + */ + OptimizedObjectInputStream(GridDataInput in) throws IOException { + this.in = in; + } + + /** + * @throws IOException In case of error. + */ + OptimizedObjectInputStream() throws IOException { + // No-op. + } + + /** + * @param clsLdr Class loader. + */ + void classLoader(ClassLoader clsLdr) { + this.clsLdr = clsLdr; + } + + /** + * @return Class loader. + */ + ClassLoader classLoader() { + return clsLdr; + } + + /** + * @return Input. + */ + public GridDataInput in() { + return in; + } + + /** + * @param in Input. + */ + public void in(GridDataInput in) { + this.in = in; + } + + /** {@inheritDoc} */ + @Override public void close() throws IOException { + reset(); + + clsLdr = null; + } + + /** {@inheritDoc} */ + @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod") + @Override public void reset() throws IOException { + in.reset(); + handles.clear(); + + curObj = null; + curFields = null; + curFieldInfoList = null; + curFieldInfoMap = null; + } + + /** {@inheritDoc} */ + @Override public Object readObjectOverride() throws ClassNotFoundException, IOException { + curObj = null; + curFields = null; + curFieldInfoList = null; + curFieldInfoMap = null; + + byte ref = in.readByte(); + + switch (ref) { + case NULL: + return null; + + case HANDLE: + return handles.lookup(readInt()); + + case OBJECT: + OptimizedClassDescriptor desc = OptimizedClassResolver.readClass(this, clsLdr); + + curCls = desc.describedClass(); + + return desc.read(this); + + default: + SB msg = new SB("Unexpected error occurred during unmarshalling"); + + if (curCls != null) + msg.a(" of an instance of the class: ").a(curCls.getName()); + - msg.a(". Check that all nodes are running the same version of GridGain and that all nodes have " + ++ msg.a(". Check that all nodes are running the same version of Ignite and that all nodes have " + + "GridOptimizedMarshaller configured with identical optimized classes lists, if any " + + "(see setClassNames and setClassNamesPath methods). If your serialized classes implement " + + "java.io.Externalizable interface, verify that serialization logic is correct."); + + throw new IOException(msg.toString()); + } + } + + /** + * Reads array from this stream. + * + * @param compType Array component type. + * @return Array. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + <T> T[] readArray(Class<T> compType) throws ClassNotFoundException, IOException { + int len = in.readInt(); + + T[] arr = (T[])Array.newInstance(compType, len); + + handles.assign(arr); + + for (int i = 0; i < len; i++) + arr[i] = (T)readObject(); + + return arr; + } + + /** + * Reads {@link UUID} from this stream. + * + * @return UUID. + * @throws IOException In case of error. + */ + UUID readUuid() throws IOException { + UUID uuid = new UUID(readLong(), readLong()); + + handles.assign(uuid); + + return uuid; + } + + /** + * Reads {@link Properties} from this stream. + * + * @return Properties. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + Properties readProperties() throws ClassNotFoundException, IOException { + Properties dflts = readBoolean() ? null : (Properties)readObject(); + + Properties props = new Properties(dflts); + + int size = in.readInt(); + + for (int i = 0; i < size; i++) + props.setProperty(readUTF(), readUTF()); + + handles.assign(props); + + return props; + } + + /** + * Reads and sets all non-static and non-transient field values from this stream. + * + * @param obj Object. + * @param fieldOffs Field offsets. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + @SuppressWarnings("ForLoopReplaceableByForEach") + void readFields(Object obj, List<T2<OptimizedFieldType, Long>> fieldOffs) throws ClassNotFoundException, + IOException { + for (int i = 0; i < fieldOffs.size(); i++) { + T2<OptimizedFieldType, Long> t = fieldOffs.get(i); + + switch ((t.get1())) { + case BYTE: + setByte(obj, t.get2(), readByte()); + + break; + + case SHORT: + setShort(obj, t.get2(), readShort()); + + break; + + case INT: + setInt(obj, t.get2(), readInt()); + + break; + + case LONG: + setLong(obj, t.get2(), readLong()); + + break; + + case FLOAT: + setFloat(obj, t.get2(), readFloat()); + + break; + + case DOUBLE: + setDouble(obj, t.get2(), readDouble()); + + break; + + case CHAR: + setChar(obj, t.get2(), readChar()); + + break; + + case BOOLEAN: + setBoolean(obj, t.get2(), readBoolean()); + + break; + + case OTHER: + setObject(obj, t.get2(), readObject()); + } + } + } + + /** + * Reads {@link Externalizable} object. + * + * @param constructor Constructor. + * @param readResolveMtd {@code readResolve} method. + * @return Object. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + Object readExternalizable(Constructor<?> constructor, Method readResolveMtd) + throws ClassNotFoundException, IOException { + Object obj; + + try { + obj = constructor.newInstance(); + } + catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { + throw new IOException(e); + } + + int handle = handles.assign(obj); + + Externalizable extObj = ((Externalizable)obj); + + extObj.readExternal(this); + + if (readResolveMtd != null) { + try { + obj = readResolveMtd.invoke(obj); + + handles.set(handle, obj); + } + catch (IllegalAccessException | InvocationTargetException e) { + throw new IOException(e); + } + } + + return obj; + } + + /** + * Reads serializable object. + * + * @param cls Class. + * @param mtds {@code readObject} methods. + * @param readResolveMtd {@code readResolve} method. + * @param fields class fields details. + * @return Object. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + @SuppressWarnings("ForLoopReplaceableByForEach") + Object readSerializable(Class<?> cls, List<Method> mtds, Method readResolveMtd, + OptimizedClassDescriptor.Fields fields) throws ClassNotFoundException, IOException { + Object obj; + + try { + obj = UNSAFE.allocateInstance(cls); + } + catch (InstantiationException e) { + throw new IOException(e); + } + + int handle = handles.assign(obj); + + for (int i = 0; i < mtds.size(); i++) { + Method mtd = mtds.get(i); + + if (mtd != null) { + curObj = obj; + curFields = fields.fieldOffs(i); + curFieldInfoList = fields.fieldInfoList(i); + curFieldInfoMap = fields.fieldInfoMap(i); + + try { + mtd.invoke(obj, this); + } + catch (IllegalAccessException | InvocationTargetException e) { + throw new IOException(e); + } + } + else + readFields(obj, fields.fieldOffs(i)); + } + + if (readResolveMtd != null) { + try { + obj = readResolveMtd.invoke(obj); + + handles.set(handle, obj); + } + catch (IllegalAccessException | InvocationTargetException e) { + throw new IOException(e); + } + } + + return obj; + } + + /** + * Reads {@link ArrayList}. + * + * @return List. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + ArrayList<?> readArrayList() throws ClassNotFoundException, IOException { + int size = readInt(); + + ArrayList<Object> list = new ArrayList<>(size); + + handles.assign(list); + + for (int i = 0; i < size; i++) + list.add(readObject()); + + return list; + } + + /** + * Reads {@link HashMap}. + * + * @param set Whether reading underlying map from {@link HashSet}. + * @return Map. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + HashMap<?, ?> readHashMap(boolean set) throws ClassNotFoundException, IOException { + int size = readInt(); + float loadFactor = readFloat(); + + HashMap<Object, Object> map = new HashMap<>(size, loadFactor); + + if (!set) + handles.assign(map); + + for (int i = 0; i < size; i++) { + Object key = readObject(); + Object val = !set ? readObject() : DUMMY; + + map.put(key, val); + } + + return map; + } + + /** + * Reads {@link HashSet}. + * + * @param mapFieldOff Map field offset. + * @return Set. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + HashSet<?> readHashSet(long mapFieldOff) throws ClassNotFoundException, IOException { + try { + HashSet<Object> set = (HashSet<Object>)UNSAFE.allocateInstance(HashSet.class); + + handles.assign(set); + + setObject(set, mapFieldOff, readHashMap(true)); + + return set; + } + catch (InstantiationException e) { + throw new IOException(e); + } + } + + /** + * Reads {@link LinkedList}. + * + * @return List. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + LinkedList<?> readLinkedList() throws ClassNotFoundException, IOException { + int size = readInt(); + + LinkedList<Object> list = new LinkedList<>(); + + handles.assign(list); + + for (int i = 0; i < size; i++) + list.add(readObject()); + + return list; + } + + /** + * Reads {@link LinkedHashMap}. + * + * @param set Whether reading underlying map from {@link LinkedHashSet}. + * @return Map. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + LinkedHashMap<?, ?> readLinkedHashMap(boolean set) throws ClassNotFoundException, IOException { + int size = readInt(); + float loadFactor = readFloat(); + boolean accessOrder = readBoolean(); + + LinkedHashMap<Object, Object> map = new LinkedHashMap<>(size, loadFactor, accessOrder); + + if (!set) + handles.assign(map); + + for (int i = 0; i < size; i++) { + Object key = readObject(); + Object val = !set ? readObject() : DUMMY; + + map.put(key, val); + } + + return map; + } + + /** + * Reads {@link LinkedHashSet}. + * + * @param mapFieldOff Map field offset. + * @return Set. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + LinkedHashSet<?> readLinkedHashSet(long mapFieldOff) throws ClassNotFoundException, IOException { + try { + LinkedHashSet<Object> set = (LinkedHashSet<Object>)UNSAFE.allocateInstance(LinkedHashSet.class); + + handles.assign(set); + + setObject(set, mapFieldOff, readLinkedHashMap(true)); + + return set; + } + catch (InstantiationException e) { + throw new IOException(e); + } + } + + /** + * Reads {@link Date}. + * + * @return Date. + * @throws ClassNotFoundException If class not found. + * @throws IOException In case of error. + */ + Date readDate() throws ClassNotFoundException, IOException { + Date date = new Date(readLong()); + + handles.assign(date); + + return date; + } + + /** + * Reads array of {@code byte}s. + * + * @return Array. + * @throws IOException In case of error. + */ + byte[] readByteArray() throws IOException { + byte[] arr = in.readByteArray(); + + handles.assign(arr); + + return arr; + } + + /** + * Reads array of {@code short}s. + * + * @return Array. + * @throws IOException In case of error. + */ + short[] readShortArray() throws IOException { + short[] arr = in.readShortArray(); + + handles.assign(arr); + + return arr; + } + + /** + * Reads array of {@code int}s. + * + * @return Array. + * @throws IOException In case of error. + */ + int[] readIntArray() throws IOException { + int[] arr = in.readIntArray(); + + handles.assign(arr); + + return arr; + } + + /** + * Reads array of {@code long}s. + * + * @return Array. + * @throws IOException In case of error. + */ + long[] readLongArray() throws IOException { + long[] arr = in.readLongArray(); + + handles.assign(arr); + + return arr; + } + + /** + * Reads array of {@code float}s. + * + * @return Array. + * @throws IOException In case of error. + */ + float[] readFloatArray() throws IOException { + float[] arr = in.readFloatArray(); + + handles.assign(arr); + + return arr; + } + + /** + * Reads array of {@code double}s. + * + * @return Array. + * @throws IOException In case of error. + */ + double[] readDoubleArray() throws IOException { + double[] arr = in.readDoubleArray(); + + handles.assign(arr); + + return arr; + } + + /** + * Reads array of {@code char}s. + * + * @return Array. + * @throws IOException In case of error. + */ + char[] readCharArray() throws IOException { + char[] arr = in.readCharArray(); + + handles.assign(arr); + + return arr; + } + + /** + * Reads array of {@code boolean}s. + * + * @return Array. + * @throws IOException In case of error. + */ + boolean[] readBooleanArray() throws IOException { + boolean[] arr = in.readBooleanArray(); + + handles.assign(arr); + + return arr; + } + + /** + * Reads {@link String}. + * + * @return String. + * @throws IOException In case of error. + */ + public String readString() throws IOException { + String str = in.readUTF(); + + handles.assign(str); + + return str; + } + + /** {@inheritDoc} */ + @Override public void readFully(byte[] b) throws IOException { + in.readFully(b); + } + + /** {@inheritDoc} */ + @Override public void readFully(byte[] b, int off, int len) throws IOException { + in.readFully(b, off, len); + } + + /** {@inheritDoc} */ + @Override public int skipBytes(int n) throws IOException { + return in.skipBytes(n); + } + + /** {@inheritDoc} */ + @Override public boolean readBoolean() throws IOException { + return in.readBoolean(); + } + + /** {@inheritDoc} */ + @Override public byte readByte() throws IOException { + return in.readByte(); + } + + /** {@inheritDoc} */ + @Override public int readUnsignedByte() throws IOException { + return in.readUnsignedByte(); + } + + /** {@inheritDoc} */ + @Override public short readShort() throws IOException { + return in.readShort(); + } + + /** {@inheritDoc} */ + @Override public int readUnsignedShort() throws IOException { + return in.readUnsignedShort(); + } + + /** {@inheritDoc} */ + @Override public char readChar() throws IOException { + return in.readChar(); + } + + /** {@inheritDoc} */ + @Override public int readInt() throws IOException { + return in.readInt(); + } + + /** {@inheritDoc} */ + @Override public long readLong() throws IOException { + return in.readLong(); + } + + /** {@inheritDoc} */ + @Override public float readFloat() throws IOException { + return in.readFloat(); + } + + /** {@inheritDoc} */ + @Override public double readDouble() throws IOException { + return in.readDouble(); + } + + /** {@inheritDoc} */ + @Override public int read() throws IOException { + return in.read(); + } + + /** {@inheritDoc} */ + @Override public int read(byte[] b) throws IOException { + return in.read(b); + } + + /** {@inheritDoc} */ + @Override public int read(byte[] b, int off, int len) throws IOException { + return in.read(b, off, len); + } + + /** {@inheritDoc} */ + @SuppressWarnings("deprecation") + @Override public String readLine() throws IOException { + return in.readLine(); + } + + /** {@inheritDoc} */ + @Override public String readUTF() throws IOException { + return in.readUTF(); + } + + /** {@inheritDoc} */ + @Override public Object readUnshared() throws IOException, ClassNotFoundException { + return readObject(); + } + + /** {@inheritDoc} */ + @Override public void defaultReadObject() throws IOException, ClassNotFoundException { + if (curObj == null) + throw new NotActiveException("Not in readObject() call."); + + readFields(curObj, curFields); + } + + /** {@inheritDoc} */ + @Override public ObjectInputStream.GetField readFields() throws IOException, ClassNotFoundException { + if (curObj == null) + throw new NotActiveException("Not in readObject() call."); + + return new GetFieldImpl(this); + } + + /** {@inheritDoc} */ + @Override public void registerValidation(ObjectInputValidation obj, int pri) { + // No-op. + } + + /** {@inheritDoc} */ + @Override public int available() throws IOException { + return -1; + } + + /** + * Returns objects that were added to handles table. + * Used ONLY for test purposes. + * + * @return Handled objects. + */ + Object[] handledObjects() { + return handles.entries; + } + + /** + * Lightweight identity hash table which maps objects to integer handles, + * assigned in ascending order. + */ + private static class HandleTable { + /** Array mapping handle -> object/exception (depending on status). */ + private Object[] entries; + + /** Number of handles in table. */ + private int size; + + /** + * Creates handle table with the given initial capacity. + * + * @param initCap Initial capacity. + */ + HandleTable(int initCap) { + entries = new Object[initCap]; + } + + /** + * Assigns next available handle to given object, and returns assigned + * handle. + * + * @param obj Object. + * @return Handle. + */ + int assign(Object obj) { + if (size >= entries.length) + grow(); + + entries[size] = obj; + + return size++; + } + + /** + * Assigns new object to existing handle. Old object is forgotten. + * + * @param handle Handle. + * @param obj Object. + */ + void set(int handle, Object obj) { + entries[handle] = obj; + } + + /** + * Looks up and returns object associated with the given handle. + * + * @param handle Handle. + * @return Object. + */ + Object lookup(int handle) { + return entries[handle]; + } + + /** + * Resets table to its initial state. + */ + void clear() { + Arrays.fill(entries, 0, size, null); + + size = 0; + } + + /** + * Expands capacity of internal arrays. + */ + private void grow() { + int newCap = (entries.length << 1) + 1; + + Object[] newEntries = new Object[newCap]; + + System.arraycopy(entries, 0, newEntries, 0, size); + + entries = newEntries; + } + } + + /** + * {@link GetField} implementation. + */ + private static class GetFieldImpl extends GetField { + /** Field info map. */ + private final Map<String, IgniteBiTuple<Integer, OptimizedFieldType>> fieldInfoMap; + + /** Values. */ + private final Object[] objs; + + /** + * @param in Stream. + * @throws IOException In case of error. + * @throws ClassNotFoundException If class not found. + */ + @SuppressWarnings("ForLoopReplaceableByForEach") + private GetFieldImpl(OptimizedObjectInputStream in) throws IOException, ClassNotFoundException { + fieldInfoMap = in.curFieldInfoMap; + + List<IgniteBiTuple<Integer, OptimizedFieldType>> infos = in.curFieldInfoList; + + objs = new Object[infos.size()]; + + for (int i = 0; i < infos.size(); i++) { + IgniteBiTuple<Integer, OptimizedFieldType> t = infos.get(i); + + Object obj = null; + + switch (t.get2()) { + case BYTE: + obj = in.readByte(); + + break; + + case SHORT: + obj = in.readShort(); + + break; + + case INT: + obj = in.readInt(); + + break; + + case LONG: + obj = in.readLong(); + + break; + + case FLOAT: + obj = in.readFloat(); + + break; + + case DOUBLE: + obj = in.readDouble(); + + break; + + case CHAR: + obj = in.readChar(); + + break; + + case BOOLEAN: + obj = in.readBoolean(); + + break; + + case OTHER: + obj = in.readObject(); + } + + objs[t.get1()] = obj; + } + } + + /** {@inheritDoc} */ + @Override public ObjectStreamClass getObjectStreamClass() { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public boolean defaulted(String name) throws IOException { + return objs[fieldInfoMap.get(name).get1()] == null; + } + + /** {@inheritDoc} */ + @Override public boolean get(String name, boolean dflt) throws IOException { + return value(name, dflt); + } + + /** {@inheritDoc} */ + @Override public byte get(String name, byte dflt) throws IOException { + return value(name, dflt); + } + + /** {@inheritDoc} */ + @Override public char get(String name, char dflt) throws IOException { + return value(name, dflt); + } + + /** {@inheritDoc} */ + @Override public short get(String name, short dflt) throws IOException { + return value(name, dflt); + } + + /** {@inheritDoc} */ + @Override public int get(String name, int dflt) throws IOException { + return value(name, dflt); + } + + /** {@inheritDoc} */ + @Override public long get(String name, long dflt) throws IOException { + return value(name, dflt); + } + + /** {@inheritDoc} */ + @Override public float get(String name, float dflt) throws IOException { + return value(name, dflt); + } + + /** {@inheritDoc} */ + @Override public double get(String name, double dflt) throws IOException { + return value(name, dflt); + } + + /** {@inheritDoc} */ + @Override public Object get(String name, Object dflt) throws IOException { + return value(name, dflt); + } + + /** + * @param name Field name. + * @param dflt Default value. + * @return Value. + */ + private <T> T value(String name, T dflt) { + return objs[fieldInfoMap.get(name).get1()] != null ? (T)objs[fieldInfoMap.get(name).get1()] : dflt; + } + } +}