001/* 002 * This file is part of the Kompics component model runtime. 003 * 004 * Copyright (C) 2009 Swedish Institute of Computer Science (SICS) 005 * Copyright (C) 2009 Royal Institute of Technology (KTH) 006 * 007 * This program is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU General Public License 009 * as published by the Free Software Foundation; either version 2 010 * of the License, or (at your option) any later version. 011 * 012 * This program is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 020 */ 021package se.sics.kompics.network.netty.serialization; 022 023import java.util.Optional; 024import com.google.common.io.Closer; 025import io.netty.buffer.ByteBuf; 026import io.netty.buffer.ByteBufInputStream; 027import io.netty.buffer.ByteBufOutputStream; 028import io.netty.handler.codec.serialization.ClassResolver; 029import java.io.EOFException; 030import java.io.IOException; 031import java.io.InputStream; 032import java.io.ObjectInputStream; 033import java.io.ObjectOutputStream; 034import java.io.ObjectStreamClass; 035import java.io.OutputStream; 036import java.io.StreamCorruptedException; 037 038/** 039 * 040 * @author Lars Kroll {@literal <[email protected]>} 041 */ 042public class JavaSerializer implements Serializer { 043 044 private ClassResolver resolver; 045 046 public JavaSerializer(ClassResolver resolver) { 047 this.resolver = resolver; 048 } 049 050 @Override 051 public int identifier() { 052 return 3; 053 } 054 055 @Override 056 public void toBinary(Object o, ByteBuf buf) { 057 try { 058 Closer closer = Closer.create(); // TODO: Convert to try-with-resources once Java6 is faded out 059 try { 060 ByteBufOutputStream bout = closer.register(new ByteBufOutputStream(buf)); 061 ObjectOutputStream oout = closer.register(new CompactObjectOutputStream(bout)); 062 oout.writeObject(o); 063 oout.flush(); 064 } catch (Throwable e) { // must catch Throwable 065 throw closer.rethrow(e); 066 } finally { 067 closer.close(); 068 } 069 } catch (IOException ex) { 070 Serializers.LOG.error("JavaSerializer: Could not Serialize object of type " + o.getClass(), ex); 071 } 072 } 073 074 @Override 075 public Object fromBinary(ByteBuf buf, Optional<Object> hint) { 076 // Ignore hint 077 try { 078 Closer closer = Closer.create(); 079 try { 080 ByteBufInputStream bbis = closer.register(new ByteBufInputStream(buf)); 081 CompactObjectInputStream cois = closer.register(new CompactObjectInputStream(bbis, resolver)); 082 return cois.readObject(); 083 } catch (Throwable e) { 084 throw closer.rethrow(e); 085 } finally { 086 closer.close(); 087 } 088 } catch (IOException ex) { 089 Serializers.LOG.error("JavaSerializer: Could not deserialize object", ex); 090 return null; 091 } 092 } 093 094 private static class CompactObjectInputStream extends ObjectInputStream { 095 096 private final ClassResolver classResolver; 097 098 CompactObjectInputStream(InputStream in, ClassResolver classResolver) throws IOException { 099 super(in); 100 this.classResolver = classResolver; 101 } 102 103 @Override 104 protected void readStreamHeader() throws IOException { 105 int version = readByte() & 0xFF; 106 if (version != STREAM_VERSION) { 107 throw new StreamCorruptedException("Unsupported version: " + version); 108 } 109 } 110 111 @Override 112 protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { 113 int type = read(); 114 if (type < 0) { 115 throw new EOFException(); 116 } 117 switch (type) { 118 case CompactObjectOutputStream.TYPE_FAT_DESCRIPTOR: 119 return super.readClassDescriptor(); 120 case CompactObjectOutputStream.TYPE_THIN_DESCRIPTOR: 121 String className = readUTF(); 122 Class<?> clazz = classResolver.resolve(className); 123 return ObjectStreamClass.lookupAny(clazz); 124 default: 125 throw new StreamCorruptedException("Unexpected class descriptor type: " + type); 126 } 127 } 128 129 @Override 130 protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { 131 Class<?> clazz; 132 try { 133 clazz = classResolver.resolve(desc.getName()); 134 } catch (ClassNotFoundException ex) { 135 clazz = super.resolveClass(desc); 136 } 137 138 return clazz; 139 } 140 141 } 142 143 private static class CompactObjectOutputStream extends ObjectOutputStream { 144 145 static final byte TYPE_FAT_DESCRIPTOR = 0; 146 static final byte TYPE_THIN_DESCRIPTOR = 1; 147 148 CompactObjectOutputStream(OutputStream out) throws IOException { 149 super(out); 150 } 151 152 @Override 153 protected void writeStreamHeader() throws IOException { 154 writeByte(STREAM_VERSION); 155 } 156 157 @Override 158 protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { 159 Class<?> clazz = desc.forClass(); 160 if (clazz.isPrimitive() || clazz.isArray() || clazz.isInterface() || desc.getSerialVersionUID() == 0) { 161 writeByte(TYPE_FAT_DESCRIPTOR); 162 super.writeClassDescriptor(desc); 163 } else { 164 writeByte(TYPE_THIN_DESCRIPTOR); 165 writeUTF(desc.getName()); 166 } 167 } 168 } 169 170}