001/*
002 * JPPF.
003 * Copyright (C) 2005-2018 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.jppf.serialization;
020
021import java.io.*;
022import java.util.Map;
023
024/**
025 * Implementation of {@link ObjectOutputStream} that writes objects without regards to whether
026 * they implement {@link Serializable} or not. This allows using non-serializable classes in
027 * JPPF tasks, especially when their source code is not available.
028 * <p>The rest of the {@link ObjectOutputStream} specification is respected:
029 * <ul>
030 * <li>transient fields are not serialized</li>
031 * <li><code>private void writeObject(ObjectOutputStream)</code> is used whenever implemented</li>
032 * <li>the {@link Externalizable} interface is respected</li>
033 * </ul>
034 * @author Laurent Cohen
035 */
036public class JPPFObjectOutputStream extends ObjectOutputStream {
037  /**
038   * The stream serialized data is written to.
039   */
040  private DataOutputStream out;
041  /**
042   * Determines whether the stream is already writing an object graph.
043   */
044  private boolean writingObject = false;
045  /**
046   * The object graph serializer.
047   */
048  private Serializer serializer = null;
049  /**
050   * Temporary buffer to write primitive types.
051   */
052  private final byte[] buf = new byte[16];
053  /**
054   * The latest generated PutField instance.
055   */
056  private PutField currentPutField = null;
057
058  /**
059   * Initialize this object stream.
060   * @param out the stream to write objects to.
061   * @throws IOException if any error occurs.
062   */
063  public JPPFObjectOutputStream(final OutputStream out) throws IOException {
064    super();
065    this.out = (out instanceof DataOutputStream) ? (DataOutputStream) out : new DataOutputStream(out);
066    serializer = new Serializer(this);
067    write(Serializer.HEADER);
068  }
069
070  /**
071   * Initialize this object stream.
072   * @param out the stream to write objects to.
073   * @param serializer the serializer to use.
074   * @throws IOException if any error occurs.
075   */
076  public JPPFObjectOutputStream(final OutputStream out, final Serializer serializer) throws IOException {
077    super();
078    this.out = (out instanceof DataOutputStream) ? (DataOutputStream) out : new DataOutputStream(out);
079    this.serializer = serializer;
080    serializer.out = this;
081    write(Serializer.HEADER);
082  }
083
084  @Override
085  protected final void writeObjectOverride(final Object obj) throws IOException {
086    final boolean alreadyWriting = writingObject;
087    try {
088      if (!alreadyWriting) writingObject = true;
089      serializer.writeObject(obj);
090    } catch (final Exception e) {
091      throw (e instanceof IOException) ? (IOException) e : new IOException(e.getMessage(), e);
092    } finally {
093      if (!alreadyWriting) {
094        writingObject = false;
095        flush();
096      }
097    }
098  }
099
100  @Override
101  public void write(final int val) throws IOException {
102    out.write(val);
103  }
104
105  @Override
106  public void write(final byte[] buf) throws IOException {
107    out.write(buf);
108  }
109
110  @Override
111  public void write(final byte[] buf, final int off, final int len) throws IOException {
112    out.write(buf, off, len);
113  }
114
115  @Override
116  public void writeBoolean(final boolean val) throws IOException {
117    out.writeBoolean(val);
118  }
119
120  @Override
121  public void writeByte(final int val) throws IOException {
122    out.writeByte(val);
123  }
124
125  @Override
126  public void writeShort(final int val) throws IOException {
127    SerializationUtils.writeShort((short) val, buf, 0);
128    out.write(buf, 0, 2);
129  }
130
131  @Override
132  public void writeChar(final int val) throws IOException {
133    SerializationUtils.writeChar((char) val, buf, 0);
134    out.write(buf, 0, 2);
135  }
136
137  @Override
138  public void writeInt(final int val) throws IOException {
139    SerializationUtils.writeInt(val, buf, 0);
140    out.write(buf, 0, 4);
141    //SerializationUtils.writeVarInt(out, val, buf);
142  }
143
144  @Override
145  public void writeLong(final long val) throws IOException {
146    SerializationUtils.writeLong(val, buf, 0);
147    out.write(buf, 0, 8);
148  }
149
150  @Override
151  public void writeFloat(final float val) throws IOException {
152    writeInt(Float.floatToIntBits(val));
153  }
154
155  @Override
156  public void writeDouble(final double val) throws IOException {
157    writeLong(Double.doubleToLongBits(val));
158  }
159
160  @Override
161  public void writeBytes(final String str) throws IOException {
162    out.writeBytes(str);
163  }
164
165  @Override
166  public void writeChars(final String str) throws IOException {
167    out.writeChars(str);
168  }
169
170  @Override
171  public void writeUTF(final String str) throws IOException {
172    out.writeUTF(str);
173  }
174
175  @Override
176  public void defaultWriteObject() throws IOException {
177    try {
178      serializer.writeDeclaredFields(serializer.currentObject, serializer.currentClassDescriptor);
179    } catch (final Exception e) {
180      if (e instanceof IOException) throw (IOException) e;
181      else throw new IOException(e.getMessage(), e);
182    }
183  }
184
185  @Override
186  public void flush() throws IOException {
187    out.flush();
188  }
189
190  @Override
191  public void close() throws IOException {
192    out.close();
193  }
194
195  @Override
196  public PutField putFields() throws IOException {
197    if (currentPutField == null) currentPutField = new JPPFPutField(this);
198    return currentPutField;
199  }
200
201  @Override
202  public void writeFields() throws IOException {
203    try {
204      final JPPFPutField f = (JPPFPutField) currentPutField;
205      writeFields0(f.primitiveFields);
206      writeFields0(f.objectFields);
207    } catch (final Exception e) {
208      if (e instanceof IOException) throw (IOException) e;
209      else throw new IOException(e);
210    }
211  }
212
213  /**
214   * Write the primitive or object fields of the current PutField.
215   * @param map the map containing fields names and values.
216   * @throws Exception if any error occurs.
217   */
218  private void writeFields0(final Map<String, Object> map) throws Exception {
219    writeInt(map.size());
220    for (Map.Entry<String, Object> entry : map.entrySet()) {
221      serializer.writeObject(entry.getKey());
222      serializer.writeObject(entry.getValue());
223    }
224  }
225}