All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jppf.serialization.Serializer Maven / Gradle / Ivy

There is a newer version: 6.3-alpha
Show newest version
/*
 * JPPF.
 * Copyright (C) 2005-2015 JPPF Team.
 * http://www.jppf.org
 *
 * Licensed 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.jppf.serialization;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * Instances of this class are intended to serialize object graphs to an underlying output stream.
 * @author Laurent Cohen
 * @exclude
 */
class Serializer {
  /**
   * The stream header ('JPPF' in ascii).
   */
  static final byte[] HEADER = { 74, 80, 80, 70 };
  /**
   * Handle for null references.
   */
  static final byte[] NULL_HANDLE = { 0, 0, 0, 0 };
  /**
   * Header written before a class descriptor.
   */
  static final byte CLASS_HEADER = 1;
  /**
   * Header written before a serialized object.
   */
  static final byte OBJECT_HEADER = 2;
  /**
   * Header written before a serialized object.
   */
  static final byte NULL_OBJECT_HEADER = 3;
  /**
   * Special treatment when an object to serialize is a class.
   */
  static final byte CLASS_OBJECT_HEADER = 4;
  /**
   * The stream serialized data is written to.
   */
  ObjectOutputStream out;
  /**
   * Holds all class and object descriptors.
   */
  SerializationCaches caches = new SerializationCaches();
  /**
   * Descriptor the class of the object currently being written.
   */
  ClassDescriptor currentClassDescriptor;
  /**
   * The object currently being written.
   */
  Object currentObject;
  /**
   * Temporary buffer used to write arrays of primitive values to the stream.
   */
  private byte[] buf = new byte[4096];

  /**
   * Initialize this serializer with the specified output stream, and write the header.
   * @param out the stream to which the serialized data is written.
   * @throws IOException if an error occurs while writing the header.
   */
  Serializer(final ObjectOutputStream out) throws IOException {
    this.out = out;
    out.write(HEADER);
  }

  /**
   * Write the specified object to the output stream.
   * @param obj the object to write.
   * @throws Exception if any error occurs.
   */
  void writeObject(final Object obj) throws Exception {
    if (obj == null) out.writeByte(NULL_OBJECT_HEADER);
    else if (obj instanceof Class) writeClassObject((Class) obj);
    else {
      Integer handle = caches.objectHandleMap.get(obj);
      if (handle == null) {
        handle = caches.newObjectHandle(obj);
        writeObject(obj, handle);
      } else {
        out.writeByte(OBJECT_HEADER);
        out.writeInt(handle);
      }
    }
  }

  /**
   * Write the specified object to the output stream.
   * @param obj the object to write.
   * @param handle the object's handle
   * @throws Exception if any error occurs.
   */
  private void writeObject(final Object obj, final int handle) throws Exception {
    Map, ClassDescriptor> map = new HashMap<>();
    ClassDescriptor cd = caches.getClassDescriptor(obj.getClass(), map);
    currentObject = obj;
    currentClassDescriptor = cd;
    writeClassDescriptors(map);
    map = null;
    out.writeByte(OBJECT_HEADER);
    out.writeInt(handle);
    out.writeInt(cd.handle);
    //if (traceEnabled) try { log.trace("writing object " + obj + ", handle=" + handle + ", class=" + obj.getClass() + ", cd=" + cd); } catch(Exception e) {}
    if (cd.array) writeArray(obj, cd);
    else if (cd.enumType) {
      String name = ((Enum) obj).name();
      writeObject(name);
    }
    else writeFields(obj, cd);
  }

  /**
   * Write the specified object to the output stream.
   * @param obj the object to write.
   * @throws Exception if any error occurs.
   */
  private void writeClassObject(final Class obj) throws Exception {
    Map, ClassDescriptor> map = new HashMap<>();
    ClassDescriptor cd = caches.getClassDescriptor(obj, map);
    currentObject = obj;
    currentClassDescriptor = cd;
    writeClassDescriptors(map);
    out.writeByte(CLASS_OBJECT_HEADER);
    out.writeInt(cd.handle);
  }

  /**
   * Write the all fields, including those declared in the superclasses, for the specified object.
   * @param obj the object whose fields are to be written.
   * @param cd the object's class descriptor.
   * @throws Exception if any error occurs.
   */
  void writeFields(final Object obj, final ClassDescriptor cd) throws Exception {
    ClassDescriptor tmpDesc = cd;
    Deque stack = new LinkedBlockingDeque<>();
    while (tmpDesc != null) {
      stack.addFirst(tmpDesc);
      tmpDesc = tmpDesc.superClass;
    }
    for (ClassDescriptor desc: stack) {
      if (desc.hasWriteObject) {
        Method m = SerializationReflectionHelper.getWriteObjectMethod(desc.clazz);
        if (!m.isAccessible()) m.setAccessible(true);
        //if (traceEnabled) try { log.trace("invoking writeObject() for class=" + desc + " on object " + obj.hashCode()); } catch(Exception e) { log.trace(e.getMessage(), e); }
        try {
          tmpDesc = currentClassDescriptor;
          currentClassDescriptor = desc;
          m.invoke(obj, out);
        } finally {
          currentClassDescriptor = tmpDesc;
        }
      }
      else if (desc.externalizable) ((Externalizable) obj).writeExternal(out);
      else writeDeclaredFields(obj, desc);
    }
  }

  /**
   * Write the fields for the specified object and class descriptor.
   * @param obj the object whose fields are to be written.
   * @param cd the object's class descriptor.
   * @throws Exception if any error occurs.
   */
  void writeDeclaredFields(final Object obj, final ClassDescriptor cd) throws Exception {
    for (int i=0; i, ClassDescriptor> map) throws IOException {
    if (map.isEmpty()) return;
    out.writeByte(CLASS_HEADER);
    out.writeInt(map.size());
    for (Map.Entry, ClassDescriptor> entry: map.entrySet()) entry.getValue().write(out);
  }

  /**
   * Write an array of booleans to the stream.
   * @param array the array of boolean values to write.
   * @throws Exception if any error occurs.
   */
  private void writeBooleanArray(final boolean[] array) throws Exception {
    for (int count=0; count < array.length;) {
      int n = Math.min(buf.length, array.length - count);
      for (int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy