/* * Copyright (c) 2015 Red Hat, Inc. and/or its affiliates. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.jberet.runtime; import java.io.IOException; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import javax.batch.operations.BatchRuntimeException; import org.jberet._private.BatchLogger; import org.jberet._private.BatchMessages; import org.jberet.util.BatchUtil; import org.wildfly.security.manager.WildFlySecurityManager; /** * Holds optionally serialized data. If the type of the serialized data is of specific types the data is not actually * serialized. If the type is not part of the JDK then the data is serialized. * * @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a> */ class SerializableData implements Serializable { private final byte[] serialized; private final Serializable raw; private SerializableData(final byte[] serialized, final Serializable raw) { this.serialized = serialized; this.raw = raw; } /** * Creates a new instance. * * @param data the data to be serialized * * @return a new instance * * @throws BatchRuntimeException if a failure to serialize the data occurs */ public static SerializableData of(final Serializable data) { if (data instanceof SerializableData) { return (SerializableData) data; } if (data instanceof byte[]) { return new SerializableData((byte[]) data, null); } if (data == null) { return new SerializableData(null, null); } Class<?> c = data.getClass(); if (c.isArray()) { c = c.getComponentType(); } if (requiresSerialization(c)) { try { return new SerializableData(BatchUtil.objectToBytes(data), null); } catch (IOException e) { if (data instanceof Throwable) { //if failed to serialize step exception data, try to preserve original //step exception message and stack trace final Throwable exceptionData = (Throwable) data; BatchLogger.LOGGER.failedToSerializeException(e, exceptionData); final BatchRuntimeException replacementException = new BatchRuntimeException(exceptionData.getMessage()); replacementException.setStackTrace(exceptionData.getStackTrace()); try { return new SerializableData(BatchUtil.objectToBytes(replacementException), null); } catch (final IOException e1) { throw BatchMessages.MESSAGES.failedToSerialize(e1, replacementException); } } throw BatchMessages.MESSAGES.failedToSerialize(e, data); } } return new SerializableData(null, data); } /** * If the data was previously serialized it's deserialized using the TCCL. If the TCCL is not set the class loader * for this class will be used. * * @return the serialized data or {@code null} if the data was {@code null} when created * * @throws BatchRuntimeException if there was a failure to desrialize the value */ public Serializable deserialize() throws BatchRuntimeException { if (raw != null) { return raw; } if (serialized != null) { // In an EE container the TCCL should be set to the class loader required for the deployment, if it's not // set we'll default to the class loader for this class to deserialize the data ClassLoader cl = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged(); if (cl == null) { cl = WildFlySecurityManager.getClassLoaderPrivileged(SerializableData.class); } try { return BatchUtil.bytesToSerializableObject(serialized, cl); } catch (IOException e) { throw BatchMessages.MESSAGES.failedToDeserialize(e, Arrays.toString(serialized)); } catch (ClassNotFoundException e) { throw BatchMessages.MESSAGES.failedToDeserialize(e, Arrays.toString(serialized)); } } return null; } byte[] getSerialized() throws BatchRuntimeException { if (serialized != null) { return serialized; } try { return BatchUtil.objectToBytes(raw); } catch (final IOException e) { throw BatchMessages.MESSAGES.failedToSerialize(e, raw); } } @Override public int hashCode() { int hash = 17; if (raw != null) { hash = 31 * hash + raw.hashCode(); } if (serialized != null) { hash = 31 * hash + Arrays.hashCode(serialized); } return hash; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof SerializableData)) { return false; } final SerializableData other = (SerializableData) obj; return (raw == null ? other.raw == null : raw.equals(other.raw)) && Arrays.equals(serialized, other.serialized); } private static boolean requiresSerialization(final Class<?> c) { return !c.isPrimitive() && c != String.class && c != Byte.class && c != Double.class && c != Float.class && c != Integer.class && c != Long.class && c != Short.class && c != BigDecimal.class && c != BigInteger.class; } }