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; 023 024import java.io.EOFException; 025import java.io.IOException; 026import java.io.InputStream; 027import java.lang.reflect.InvocationTargetException; 028import java.util.ArrayList; 029import java.util.HashMap; 030import java.util.List; 031import java.util.Map; 032 033import org.granite.messaging.annotations.Include; 034import org.granite.messaging.jmf.codec.StandardCodec; 035import org.granite.messaging.reflect.ClassDescriptor; 036import org.granite.messaging.reflect.NoopWritableProperty; 037import org.granite.messaging.reflect.Property; 038import org.granite.messaging.reflect.Reflection; 039 040/** 041 * @author Franck WOLFF 042 */ 043public class JMFDeserializer implements InputContext { 044 045 /////////////////////////////////////////////////////////////////////////// 046 // Fields 047 048 protected final List<String> classNames = new ArrayList<String>(256); 049 protected final List<String> strings = new ArrayList<String>(256); 050 protected final List<Object> objects = new ArrayList<Object>(256); 051 052 protected final Map<String, ClassDescriptor> classDescriptors = new HashMap<String, ClassDescriptor>(256); 053 054 protected final InputStream inputStream; 055 protected final SharedContext context; 056 057 protected final CodecRegistry codecRegistry; 058 059 /////////////////////////////////////////////////////////////////////////// 060 // Initialization 061 062 public JMFDeserializer(InputStream is, SharedContext context) { 063 this.inputStream = is; 064 this.context = context; 065 this.codecRegistry = context.getCodecRegistry(); 066 067 this.classNames.addAll(context.getInitialClassNameDictionary()); 068 } 069 070 /////////////////////////////////////////////////////////////////////////// 071 // ObjectInput implementation 072 073 public boolean readBoolean() throws IOException { 074 return codecRegistry.getBooleanCodec().decodePrimitive(this); 075 } 076 077 public byte readByte() throws IOException { 078 return codecRegistry.getByteCodec().decodePrimitive(this); 079 } 080 081 public int readUnsignedByte() throws IOException { 082 return readByte() & 0xFF; 083 } 084 085 public short readShort() throws IOException { 086 return codecRegistry.getShortCodec().decodePrimitive(this); 087 } 088 089 public int readUnsignedShort() throws IOException { 090 return readShort() & 0xFFFF; 091 } 092 093 public char readChar() throws IOException { 094 return codecRegistry.getCharacterCodec().decodePrimitive(this); 095 } 096 097 public int readInt() throws IOException { 098 return codecRegistry.getIntegerCodec().decodePrimitive(this); 099 } 100 101 public long readLong() throws IOException { 102 return codecRegistry.getLongCodec().decodePrimitive(this); 103 } 104 105 public float readFloat() throws IOException { 106 return codecRegistry.getFloatCodec().decodePrimitive(this); 107 } 108 109 public double readDouble() throws IOException { 110 return codecRegistry.getDoubleCodec().decodePrimitive(this); 111 } 112 113 public String readUTF() throws IOException { 114 int parameterizedJmfType = safeRead(); 115 116 if (parameterizedJmfType == JMF_NULL) 117 return (String)codecRegistry.getNullCodec().decode(this, parameterizedJmfType); 118 119 return codecRegistry.getStringCodec().decode(this, parameterizedJmfType); 120 } 121 122 public Object readObject() throws ClassNotFoundException, IOException { 123 int parameterizedJmfType = safeRead(); 124 125 StandardCodec<?> codec = codecRegistry.getCodec(parameterizedJmfType); 126 if (codec == null) 127 throw new JMFEncodingException("Unsupported JMF type: " + codecRegistry.extractJmfType(parameterizedJmfType)); 128 129 try { 130 return codec.decode(this, parameterizedJmfType); 131 } 132 catch (InvocationTargetException e) { 133 throw new IOException(e.getTargetException()); 134 } 135 catch (IllegalAccessException e) { 136 throw new IOException(e); 137 } 138 catch (InstantiationException e) { 139 throw new IOException(e); 140 } 141 catch (NoSuchMethodException e) { 142 throw new IOException(e); 143 } 144 } 145 146 public int available() throws IOException { 147 return inputStream.available(); 148 } 149 150 public void close() throws IOException { 151 inputStream.close(); 152 } 153 154 /////////////////////////////////////////////////////////////////////////// 155 // ObjectInput implementation (unsupported, marked at deprecated) 156 157 @Deprecated 158 public int read() throws IOException { 159 throw new UnsupportedOperationException("Use readByte()"); 160 } 161 162 @Deprecated 163 public int read(byte[] b) throws IOException { 164 throw new UnsupportedOperationException("Use (byte[])readObject()"); 165 } 166 167 @Deprecated 168 public int read(byte[] b, int off, int len) throws IOException { 169 throw new UnsupportedOperationException("Use (byte[])readObject()"); 170 } 171 172 @Deprecated 173 public void readFully(byte[] b) throws IOException { 174 throw new UnsupportedOperationException("Use (byte[])readObject()"); 175 } 176 177 @Deprecated 178 public void readFully(byte[] b, int off, int len) throws IOException { 179 throw new UnsupportedOperationException("Use (byte[])readObject()"); 180 } 181 182 @Deprecated 183 public String readLine() throws IOException { 184 throw new UnsupportedOperationException("Use readUTF()"); 185 } 186 187 @Deprecated 188 public int skipBytes(int n) throws IOException { 189 throw new UnsupportedOperationException(); 190 } 191 192 @Deprecated 193 public long skip(long n) throws IOException { 194 throw new UnsupportedOperationException(); 195 } 196 197 /////////////////////////////////////////////////////////////////////////// 198 // InputContext implementation 199 200 public SharedContext getSharedContext() { 201 return context; 202 } 203 204 public InputStream getInputStream() { 205 return inputStream; 206 } 207 208 public int safeRead() throws IOException { 209 int b = inputStream.read(); 210 if (b == -1) 211 throw new EOFException(); 212 return b; 213 } 214 215 public long safeReadLong() throws IOException { 216 return (long)safeRead(); 217 } 218 219 public void safeReadFully(byte[] b) throws IOException { 220 safeReadFully(b, 0, b.length); 221 } 222 223 public void safeReadFully(byte[] b, int off, int len) throws IOException { 224 if (off < 0 || len < 0 || off + len > b.length) 225 throw new IndexOutOfBoundsException("b.length=" + b.length + ", off=" + off + ", len" + len); 226 227 if (len == 0) 228 return; 229 230 do { 231 int read = inputStream.read(b, off, len); 232 if (read == -1) 233 throw new EOFException(); 234 off += read; 235 len -= read; 236 } 237 while (len > 0); 238 } 239 240 public void safeSkip(long n) throws IOException { 241 while (n > 0) { 242 if (inputStream.read() == -1) 243 throw new EOFException(); 244 n--; 245 } 246 } 247 248 @Override 249 public int addToClassNames(String cn) { 250 int index = classNames.size(); 251 classNames.add(index, cn); 252 return index; 253 } 254 255 @Override 256 public String getClassName(int index) { 257 return classNames.get(index); 258 } 259 260 @Override 261 public ClassDescriptor getClassDescriptor(String className) throws ClassNotFoundException { 262 ClassDescriptor desc = classDescriptors.get(className); 263 if (desc == null) { 264 Class<?> cls = context.getReflection().loadClass(className); 265 desc = context.getReflection().getDescriptor(cls); 266 classDescriptors.put(className, desc); 267 } 268 return desc; 269 } 270 271 @Override 272 public ClassDescriptor getClassDescriptor(Class<?> cls) { 273 ClassDescriptor desc = classDescriptors.get(cls.getName()); 274 if (desc == null) { 275 desc = context.getReflection().getDescriptor(cls); 276 classDescriptors.put(cls.getName(), desc); 277 } 278 return desc; 279 } 280 281 public int addToStrings(String s) { 282 int index = strings.size(); 283 strings.add(index, s); 284 return index; 285 } 286 287 public String getString(int index) { 288 return strings.get(index); 289 } 290 291 public int addToObjects(Object o) { 292 int index = objects.size(); 293 objects.add(index, o); 294 return index; 295 } 296 297 public Object getObject(int index) { 298 Object o = objects.get(index); 299 if (o instanceof UnresolvedSharedObject) 300 throw new JMFUnresolvedSharedObjectException("Unresolved shared object: " + o); 301 return o; 302 } 303 304 public int addToUnresolvedObjects(String className) { 305 int index = objects.size(); 306 objects.add(index, new UnresolvedSharedObject(className, index)); 307 return index; 308 } 309 310 public Object setUnresolvedObject(int index, Object o) { 311 Object uso = objects.set(index, o); 312 if (!(uso instanceof UnresolvedSharedObject)) 313 throw new JMFUnresolvedSharedObjectException("Not an unresolved shared object: " + uso); 314 return uso; 315 } 316 317 /////////////////////////////////////////////////////////////////////////// 318 // ExtendedObjectInput implementation 319 320 public Reflection getReflection() { 321 return context.getReflection(); 322 } 323 324 public String getAlias(String remoteAlias) { 325 return context.getClassName(remoteAlias); 326 } 327 328 public void readAndSetProperty(Object obj, Property property) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException { 329 if (property.isAnnotationPresent(Include.class) && !property.isWritable()) 330 property = new NoopWritableProperty(property.getName(), property.getType()); 331 332 if (property.getType().isPrimitive()) 333 codecRegistry.getPrimitivePropertyCodec(property.getType()).decodePrimitive(this, obj, property); 334 else 335 property.setObject(obj, readObject()); 336 } 337 338 static class UnresolvedSharedObject { 339 340 private final String className; 341 private final int index; 342 343 public UnresolvedSharedObject(String className, int index) { 344 this.className = className; 345 this.index = index; 346 } 347 348 public String getClassName() { 349 return className; 350 } 351 352 public int getIndex() { 353 return index; 354 } 355 356 @Override 357 public String toString() { 358 return UnresolvedSharedObject.class.getName() + " {className=" + className + ", index=" + index + "}"; 359 } 360 } 361 362 /////////////////////////////////////////////////////////////////////////// 363 // Debug 364 365 public String toDumpString() { 366 StringBuilder sb = new StringBuilder(getClass().getName()); 367 sb.append(" {\n"); 368 369 sb.append(" storedClassNames=[\n"); 370 for (int i = 0; i < classNames.size(); i++) 371 sb.append(" ").append(i).append(": \"").append(classNames.get(i)).append("\"\n"); 372 sb.append(" ],\n"); 373 374 375 sb.append(" storedStrings=[\n"); 376 for (int i = 0; i < strings.size(); i++) 377 sb.append(" ").append(i).append(": \"").append(strings.get(i)).append("\"\n"); 378 sb.append(" ],\n"); 379 380 sb.append(" storedObjects=[\n"); 381 for (int i = 0; i < objects.size(); i++) { 382 Object o = objects.get(i); 383 sb.append(" ").append(i).append(": ").append(o.getClass().getName()) 384 .append("@").append(System.identityHashCode(o)).append("\n"); 385 } 386 sb.append(" ],\n"); 387 388 sb.append("}"); 389 return sb.toString(); 390 } 391}