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}