| /* |
| * 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.internal.marshaller.optimized; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.OutputStream; |
| import java.io.Serializable; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| import org.apache.ignite.IgniteCheckedException; |
| import org.apache.ignite.IgniteException; |
| import org.apache.ignite.IgniteSystemProperties; |
| import org.apache.ignite.internal.util.typedef.internal.S; |
| import org.apache.ignite.internal.util.typedef.internal.U; |
| import org.apache.ignite.marshaller.AbstractNodeNameAwareMarshaller; |
| import org.jetbrains.annotations.Nullable; |
| import sun.misc.Unsafe; |
| |
| import static org.apache.ignite.IgniteSystemProperties.IGNITE_OPTIMIZED_MARSHALLER_USE_DEFAULT_SUID; |
| |
| /** |
| * 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 OptimizedMarshaller} 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"> |
| * OptimizedMarshaller marshaller = new OptimizedMarshaller(); |
| * |
| * // Enforce Serializable interface. |
| * marshaller.setRequireSerializable(true); |
| * |
| * IgniteConfiguration cfg = new IgniteConfiguration(); |
| * |
| * // 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.apache.ignite.configuration.IgniteConfiguration" singleton="true"> |
| * ... |
| * <property name="marshaller"> |
| * <bean class="OptimizedMarshaller"> |
| * <property name="requireSerializable">true</property> |
| * </bean> |
| * </property> |
| * ... |
| * </bean> |
| * </pre> |
| * <p> |
| * <img src="http://ignite.apache.org/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 AbstractNodeNameAwareMarshaller { |
| /** Use default {@code serialVersionUID} for {@link Serializable} classes. */ |
| public static final boolean USE_DFLT_SUID = |
| IgniteSystemProperties.getBoolean(IGNITE_OPTIMIZED_MARSHALLER_USE_DEFAULT_SUID, false); |
| |
| /** Default class loader. */ |
| private final ClassLoader dfltClsLdr = getClass().getClassLoader(); |
| |
| /** Whether or not to require an object to be serializable in order to be marshalled. */ |
| private boolean requireSer = true; |
| |
| /** ID mapper. */ |
| private OptimizedMarshallerIdMapper mapper; |
| |
| /** Class descriptors by class. */ |
| private final ConcurrentMap<Class, OptimizedClassDescriptor> clsMap = new ConcurrentHashMap<>(); |
| |
| /** |
| * Creates new marshaller will all defaults. |
| * |
| * @throws IgniteException If this marshaller is not supported on the current JVM. |
| */ |
| public OptimizedMarshaller() { |
| if (!available()) |
| throw new IgniteException("Using OptimizedMarshaller on unsupported JVM version (some of " + |
| "JVM-private APIs required for the marshaller to work are missing)."); |
| } |
| |
| /** |
| * Creates new marshaller providing whether it should |
| * require {@link Serializable} interface or not. |
| * |
| * @param requireSer Whether to require {@link Serializable}. |
| */ |
| public OptimizedMarshaller(boolean requireSer) { |
| this.requireSer = requireSer; |
| } |
| |
| /** |
| * Sets whether marshaller should require {@link Serializable} interface or not. |
| * |
| * @param requireSer Whether to require {@link Serializable}. |
| * @return {@code this} for chaining. |
| */ |
| public OptimizedMarshaller setRequireSerializable(boolean requireSer) { |
| this.requireSer = requireSer; |
| |
| return this; |
| } |
| |
| /** |
| * Sets ID mapper. |
| * |
| * @param mapper ID mapper. |
| * @return {@code this} for chaining. |
| */ |
| public OptimizedMarshaller setIdMapper(OptimizedMarshallerIdMapper mapper) { |
| this.mapper = mapper; |
| |
| return this; |
| } |
| |
| /** |
| * 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. |
| * @return {@code this} for chaining. |
| */ |
| public OptimizedMarshaller setPoolSize(int poolSize) { |
| OptimizedObjectStreamRegistry.poolSize(poolSize); |
| |
| return this; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected void marshal0(@Nullable Object obj, OutputStream out) throws IgniteCheckedException { |
| assert out != null; |
| |
| OptimizedObjectOutputStream objOut = null; |
| |
| try { |
| objOut = OptimizedObjectStreamRegistry.out(); |
| |
| objOut.context(clsMap, ctx, mapper, 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 protected byte[] marshal0(@Nullable Object obj) throws IgniteCheckedException { |
| OptimizedObjectOutputStream objOut = null; |
| |
| try { |
| objOut = OptimizedObjectStreamRegistry.out(); |
| |
| objOut.context(clsMap, ctx, mapper, requireSer); |
| |
| objOut.writeObject(obj); |
| |
| return objOut.out().array(); |
| } |
| catch (Exception e) { |
| throw new IgniteCheckedException("Failed to serialize object: " + obj, e); |
| } |
| finally { |
| OptimizedObjectStreamRegistry.closeOut(objOut); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected <T> T unmarshal0(InputStream in, @Nullable ClassLoader clsLdr) throws IgniteCheckedException { |
| assert in != null; |
| |
| OptimizedObjectInputStream objIn = null; |
| |
| try { |
| objIn = OptimizedObjectStreamRegistry.in(); |
| |
| objIn.context(clsMap, ctx, mapper, clsLdr != null ? clsLdr : dfltClsLdr); |
| |
| objIn.in().inputStream(in); |
| |
| return (T)objIn.readObject(); |
| } |
| catch (ClassNotFoundException e) { |
| throw new OptimizedMarshallerInaccessibleClassException( |
| "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=" + clsLdr + ", cls=" + e.getMessage() + "]", e); |
| } |
| catch (Exception e) { |
| throw new IgniteCheckedException("Failed to deserialize object with given class loader: " + |
| "[clsLdr=" + clsLdr + ", err=" + e.getMessage() + "]", e); |
| } |
| finally { |
| OptimizedObjectStreamRegistry.closeIn(objIn); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected <T> T unmarshal0(byte[] arr, @Nullable ClassLoader clsLdr) throws IgniteCheckedException { |
| assert arr != null; |
| |
| OptimizedObjectInputStream objIn = null; |
| |
| try { |
| objIn = OptimizedObjectStreamRegistry.in(); |
| |
| objIn.context(clsMap, ctx, mapper, clsLdr != null ? clsLdr : dfltClsLdr); |
| |
| objIn.in().bytes(arr, arr.length); |
| |
| return (T)objIn.readObject(); |
| } |
| 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=" + clsLdr + ", cls=" + e.getMessage() + "]", e); |
| } |
| catch (Exception e) { |
| throw new IgniteCheckedException("Failed to deserialize object with given class loader: " + 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 { |
| Class<? extends Unsafe> unsafeCls = Unsafe.class; |
| |
| 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. |
| */ |
| @Override public void onUndeploy(ClassLoader ldr) { |
| for (Class<?> cls : clsMap.keySet()) { |
| if (ldr.equals(cls.getClassLoader())) |
| clsMap.remove(cls); |
| } |
| |
| U.clearClassCache(ldr); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public String toString() { |
| return S.toString(OptimizedMarshaller.class, this); |
| } |
| } |