001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services 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 GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.messaging.jmf.codec.std.impl; 023 024import java.io.Externalizable; 025import java.io.IOException; 026import java.io.NotSerializableException; 027import java.io.OutputStream; 028import java.io.Serializable; 029import java.lang.reflect.InvocationTargetException; 030import java.lang.reflect.Proxy; 031import java.util.List; 032 033import org.granite.messaging.jmf.CodecRegistry; 034import org.granite.messaging.jmf.DumpContext; 035import org.granite.messaging.jmf.InputContext; 036import org.granite.messaging.jmf.JMFEncodingException; 037import org.granite.messaging.jmf.OutputContext; 038import org.granite.messaging.jmf.codec.ExtendedObjectCodec; 039import org.granite.messaging.jmf.codec.StandardCodec; 040import org.granite.messaging.jmf.codec.std.ObjectCodec; 041import org.granite.messaging.reflect.Property; 042 043/** 044 * @author Franck WOLFF 045 */ 046public class ObjectCodecImpl extends AbstractIntegerStringCodec<Object> implements ObjectCodec { 047 048 protected static final StringTypeHandler TYPE_HANDLER = new StringTypeHandler() { 049 050 public int type(IntegerComponents ics, boolean reference) { 051 if (reference) 052 return 0x40 | (ics.length << 4) | JMF_OBJECT; 053 return (ics.length << 4) | JMF_OBJECT; 054 } 055 056 public int indexOrLengthBytesCount(int parameterizedJmfType) { 057 return (parameterizedJmfType >> 4) & 0x03; 058 } 059 060 public boolean isReference(int parameterizedJmfType) { 061 return (parameterizedJmfType & 0x40) != 0; 062 } 063 }; 064 065 public int getObjectType() { 066 return JMF_OBJECT; 067 } 068 069 public boolean canEncode(Object v) { 070 Class<?> cls = v.getClass(); 071 return !cls.isArray() && !cls.isEnum() && !(v instanceof Class); 072 } 073 074 public void encode(OutputContext ctx, Object v) throws IOException, IllegalAccessException, InvocationTargetException { 075 final OutputStream os = ctx.getOutputStream(); 076 077 int indexOfStoredObject = ctx.indexOfStoredObjects(v); 078 if (indexOfStoredObject >= 0) { 079 IntegerComponents ics = intComponents(indexOfStoredObject); 080 os.write(0x80 | (ics.length << 4) | JMF_OBJECT); 081 writeIntData(ctx, ics); 082 } 083 else { 084 if (!(v instanceof Serializable)) 085 throw new NotSerializableException(v.getClass().getName()); 086 087 ctx.addToStoredObjects(v); 088 089 ExtendedObjectCodec extendedCodec = ctx.getSharedContext().getCodecRegistry().findExtendedEncoder(ctx, v); 090 091 String className = ( 092 extendedCodec != null ? 093 extendedCodec.getEncodedClassName(ctx, v) : 094 ctx.getAlias(v.getClass().getName()) 095 ); 096 097 writeString(ctx, className, TYPE_HANDLER); 098 099 if (extendedCodec != null) 100 extendedCodec.encode(ctx, v); 101 else if (v instanceof Externalizable && !Proxy.isProxyClass(v.getClass())) 102 ((Externalizable)v).writeExternal(ctx); 103 else 104 encodeSerializable(ctx, (Serializable)v); 105 106 os.write(JMF_OBJECT_END); 107 } 108 } 109 110 protected void encodeSerializable(OutputContext ctx, Serializable v) throws IOException, IllegalAccessException, InvocationTargetException { 111 List<Property> properties = ctx.getReflection().findSerializableProperties(v.getClass()); 112 for (Property property : properties) 113 ctx.getAndWriteProperty(v, property); 114 } 115 116 public Object decode(InputContext ctx, int parameterizedJmfType) 117 throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, 118 InvocationTargetException, SecurityException, NoSuchMethodException { 119 120 final CodecRegistry codecRegistry = ctx.getSharedContext().getCodecRegistry(); 121 122 int jmfType = codecRegistry.extractJmfType(parameterizedJmfType); 123 if (jmfType != JMF_OBJECT) 124 throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType); 125 126 Object v = null; 127 128 int indexOrLength = readIntData(ctx, (parameterizedJmfType >> 4) & 0x03, false); 129 if ((parameterizedJmfType & 0x80) != 0) 130 v = ctx.getSharedObject(indexOrLength); 131 else { 132 String className = readString(ctx, parameterizedJmfType, indexOrLength, TYPE_HANDLER); 133 134 ExtendedObjectCodec extendedCodec = codecRegistry.findExtendedDecoder(ctx, className); 135 if (extendedCodec != null) { 136 className = extendedCodec.getDecodedClassName(ctx, className); 137 int index = ctx.addUnresolvedSharedObject(className); 138 v = extendedCodec.newInstance(ctx, className); 139 ctx.setUnresolvedSharedObject(index, v); 140 extendedCodec.decode(ctx, v); 141 } 142 else { 143 className = ctx.getAlias(className); 144 Class<?> cls = ctx.getSharedContext().getReflection().loadClass(className); 145 146 if (!Serializable.class.isAssignableFrom(cls)) 147 throw new NotSerializableException(cls.getName()); 148 149 if (Externalizable.class.isAssignableFrom(cls)) { 150 v = ctx.getReflection().newInstance(cls); 151 ctx.addSharedObject(v); 152 ((Externalizable)v).readExternal(ctx); 153 } 154 else { 155 v = ctx.getReflection().newInstance(cls); 156 ctx.addSharedObject(v); 157 decodeSerializable(ctx, (Serializable)v); 158 } 159 } 160 161 int mark = ctx.safeRead(); 162 if (mark != JMF_OBJECT_END) 163 throw new JMFEncodingException("Not a Object end marker: " + mark); 164 } 165 166 return v; 167 } 168 169 protected void decodeSerializable(InputContext ctx, Serializable v) 170 throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException { 171 172 List<Property> properties = ctx.getReflection().findSerializableProperties(v.getClass()); 173 for (Property property : properties) 174 ctx.readAndSetProperty(v, property); 175 } 176 177 public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException { 178 final CodecRegistry codecRegistry = ctx.getSharedContext().getCodecRegistry(); 179 180 int jmfType = codecRegistry.extractJmfType(parameterizedJmfType); 181 182 if (jmfType != JMF_OBJECT) 183 throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType); 184 185 int indexOrLength = readIntData(ctx, (parameterizedJmfType >> 4) & 0x03, false); 186 187 if ((parameterizedJmfType & 0x80) != 0) { 188 String className = (String)ctx.getSharedObject(indexOrLength); 189 ctx.indentPrintLn("<" + className + "@" + indexOrLength + ">"); 190 } 191 else { 192 String className = readString(ctx, parameterizedJmfType, indexOrLength, TYPE_HANDLER); 193 int indexOfStoredObject = ctx.addSharedObject(className); 194 ctx.indentPrintLn(className + "@" + indexOfStoredObject + " {"); 195 ctx.incrIndent(1); 196 197 while ((parameterizedJmfType = ctx.safeRead()) != JMF_OBJECT_END) { 198 jmfType = codecRegistry.extractJmfType(parameterizedJmfType); 199 StandardCodec<?> codec = codecRegistry.getCodec(jmfType); 200 201 if (codec == null) 202 throw new JMFEncodingException("No codec for JMF type: " + jmfType); 203 204 codec.dump(ctx, parameterizedJmfType); 205 } 206 207 ctx.incrIndent(-1); 208 ctx.indentPrintLn("}"); 209 } 210 } 211}