001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.connector.store.jpa.util; 025 026 import java.io.IOException; 027 import java.io.InputStream; 028 import java.io.ObjectInputStream; 029 import java.io.ObjectOutputStream; 030 import java.math.BigDecimal; 031 import java.net.URI; 032 import java.security.NoSuchAlgorithmException; 033 import java.util.Collection; 034 import java.util.Collections; 035 import java.util.HashMap; 036 import java.util.HashSet; 037 import java.util.Map; 038 import java.util.Set; 039 import java.util.UUID; 040 import org.jboss.dna.common.SystemFailureException; 041 import org.jboss.dna.common.util.SecureHash; 042 import org.jboss.dna.connector.store.jpa.model.basic.LargeValueEntity; 043 import org.jboss.dna.graph.DnaLexicon; 044 import org.jboss.dna.graph.ExecutionContext; 045 import org.jboss.dna.graph.property.Binary; 046 import org.jboss.dna.graph.property.BinaryFactory; 047 import org.jboss.dna.graph.property.DateTime; 048 import org.jboss.dna.graph.property.Name; 049 import org.jboss.dna.graph.property.Path; 050 import org.jboss.dna.graph.property.Property; 051 import org.jboss.dna.graph.property.PropertyFactory; 052 import org.jboss.dna.graph.property.PropertyType; 053 import org.jboss.dna.graph.property.Reference; 054 import org.jboss.dna.graph.property.UuidFactory; 055 import org.jboss.dna.graph.property.ValueFactories; 056 import org.jboss.dna.graph.property.ValueFactory; 057 import org.jboss.dna.graph.property.ValueFormatException; 058 059 /** 060 * @author Randall Hauch 061 */ 062 public class Serializer { 063 064 public static final LargeValues NO_LARGE_VALUES = new NoLargeValues(); 065 public static final ReferenceValues NO_REFERENCES_VALUES = new NoReferenceValues(); 066 067 private final PropertyFactory propertyFactory; 068 private final ValueFactories valueFactories; 069 private final boolean excludeUuidProperty; 070 071 public Serializer( ExecutionContext context, 072 boolean excludeUuidProperty ) { 073 this.propertyFactory = context.getPropertyFactory(); 074 this.valueFactories = context.getValueFactories(); 075 this.excludeUuidProperty = excludeUuidProperty; 076 } 077 078 /** 079 * Interface that represents the location where "large" objects are stored. 080 * 081 * @author Randall Hauch 082 */ 083 public interface LargeValues { 084 /** 085 * Get the minimum size for large values, specified as {@link String#length() number of characters} for a {@link String} 086 * or the {@link Binary#getSize() number of bytes for a binary value} 087 * 088 * @return the size at which a property value is considered to be <i>large</i> 089 */ 090 long getMinimumSize(); 091 092 void write( byte[] hash, 093 long length, 094 PropertyType type, 095 Object value ) throws IOException; 096 097 Object read( ValueFactories valueFactories, 098 byte[] hash, 099 long length ) throws IOException; 100 } 101 102 protected static class NoLargeValues implements LargeValues { 103 public long getMinimumSize() { 104 return Long.MAX_VALUE; 105 } 106 107 public void write( byte[] hash, 108 long length, 109 PropertyType type, 110 Object value ) { 111 } 112 113 public Object read( ValueFactories valueFactories, 114 byte[] hash, 115 long length ) { 116 return null; 117 } 118 } 119 120 /** 121 * Interface used to record how Reference values are processed during serialization and deserialization. 122 * 123 * @author Randall Hauch 124 */ 125 public interface ReferenceValues { 126 void read( Reference reference ); 127 128 void write( Reference reference ); 129 130 void remove( Reference reference ); 131 } 132 133 protected static class NoReferenceValues implements ReferenceValues { 134 public void read( Reference arg0 ) { 135 } 136 137 public void remove( Reference arg0 ) { 138 } 139 140 public void write( Reference arg0 ) { 141 } 142 } 143 144 /** 145 * Serialize the properties' values to the object stream. 146 * <p> 147 * If any of the property values are considered {@link LargeValues#getMinimumSize() large}, the value's hash and length of the 148 * property value will be written to the object stream, but the property value will be sent to the supplied 149 * {@link LargeValueEntity} object. 150 * </p> 151 * <p> 152 * This method does not automatically write each property value to the stream using 153 * {@link ObjectOutputStream#writeObject(Object)}, but instead serializes the primitive values that make up the property value 154 * object with a code that describes the {@link PropertyType property's type}. This is more efficient, since most of the 155 * property values are really non-primitive objects, and writing to the stream using 156 * {@link ObjectOutputStream#writeObject(Object)} would include larger class metadata. 157 * </p> 158 * 159 * @param stream the stream where the properties' values are to be serialized; may not be null 160 * @param number the number of properties exposed by the supplied <code>properties</code> iterator; must be 0 or positive 161 * @param properties the iterator over the properties that are to be serialized; may not be null 162 * @param largeValues the interface to use for writing large values; may not be null 163 * @param references the interface to use for recording which {@link Reference} values were found during serialization, or 164 * null if the references do not need to be accumulated 165 * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code> 166 * @see #deserializeAllProperties(ObjectInputStream, Collection, LargeValues) 167 * @see #deserializeSomeProperties(ObjectInputStream, Collection, LargeValues, LargeValues, Name...) 168 * @see #serializeProperty(ObjectOutputStream, Property, LargeValues, ReferenceValues) 169 */ 170 public void serializeProperties( ObjectOutputStream stream, 171 int number, 172 Iterable<Property> properties, 173 LargeValues largeValues, 174 ReferenceValues references ) throws IOException { 175 assert number >= 0; 176 assert properties != null; 177 assert largeValues != null; 178 stream.writeInt(number); 179 for (Property property : properties) { 180 if (property == null) continue; 181 serializeProperty(stream, property, largeValues, references); 182 } 183 } 184 185 /** 186 * Serialize the property's values to the object stream. 187 * <p> 188 * If any of the property values are considered {@link LargeValues#getMinimumSize() large}, the value's hash and length of the 189 * property value will be written to the object stream, but the property value will be sent to the supplied 190 * {@link LargeValueEntity} object. 191 * </p> 192 * <p> 193 * This method does not automatically write each property value to the stream using 194 * {@link ObjectOutputStream#writeObject(Object)}, but instead serializes the primitive values that make up the property value 195 * object with a code that describes the {@link PropertyType property's type}. This is more efficient, since most of the 196 * property values are really non-primitive objects, and writing to the stream using 197 * {@link ObjectOutputStream#writeObject(Object)} would include larger class metadata. 198 * </p> 199 * 200 * @param stream the stream where the property's values are to be serialized; may not be null 201 * @param property the property to be serialized; may not be null 202 * @param largeValues the interface to use for writing large values; may not be null 203 * @param references the interface to use for recording which {@link Reference} values were found during serialization, or 204 * null if the references do not need to be accumulated 205 * @return true if the property was serialized, or false if it was not 206 * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code> 207 * @see #serializeProperties(ObjectOutputStream, int, Iterable, LargeValues, ReferenceValues) 208 * @see #deserializePropertyValues(ObjectInputStream, Name, boolean, LargeValues, LargeValues, ReferenceValues) 209 */ 210 public boolean serializeProperty( ObjectOutputStream stream, 211 Property property, 212 LargeValues largeValues, 213 ReferenceValues references ) throws IOException { 214 assert stream != null; 215 assert property != null; 216 assert largeValues != null; 217 assert references != null; 218 final Name name = property.getName(); 219 if (this.excludeUuidProperty && DnaLexicon.UUID.equals(name)) return false; 220 // Write the name ... 221 stream.writeObject(name.getString()); 222 // Write the number of values ... 223 stream.writeInt(property.size()); 224 for (Object value : property) { 225 if (value instanceof String) { 226 String stringValue = (String)value; 227 if (largeValues != null && stringValue.length() > largeValues.getMinimumSize()) { 228 // Store the value in the large values area, but record the hash and length here. 229 byte[] hash = computeHash(stringValue); 230 stream.writeChar('L'); 231 stream.writeInt(hash.length); 232 stream.write(hash); 233 stream.writeLong(stringValue.length()); 234 // Now write to the large objects ... 235 largeValues.write(computeHash(stringValue), stringValue.length(), PropertyType.STRING, stringValue); 236 } else { 237 stream.writeChar('S'); 238 stream.writeObject(stringValue); 239 } 240 } else if (value instanceof Boolean) { 241 stream.writeChar('b'); 242 stream.writeBoolean(((Boolean)value).booleanValue()); 243 } else if (value instanceof Long) { 244 stream.writeChar('l'); 245 stream.writeLong(((Long)value).longValue()); 246 } else if (value instanceof Double) { 247 stream.writeChar('d'); 248 stream.writeDouble(((Double)value).doubleValue()); 249 } else if (value instanceof Integer) { 250 stream.writeChar('i'); 251 stream.writeInt(((Integer)value).intValue()); 252 } else if (value instanceof Short) { 253 stream.writeChar('s'); 254 stream.writeShort(((Short)value).shortValue()); 255 } else if (value instanceof Float) { 256 stream.writeChar('f'); 257 stream.writeFloat(((Float)value).floatValue()); 258 } else if (value instanceof UUID) { 259 stream.writeChar('U'); 260 UUID uuid = (UUID)value; 261 stream.writeLong(uuid.getMostSignificantBits()); 262 stream.writeLong(uuid.getLeastSignificantBits()); 263 } else if (value instanceof URI) { 264 URI uri = (URI)value; 265 String stringValue = uri.toString(); 266 if (largeValues != null && stringValue.length() > largeValues.getMinimumSize()) { 267 // Store the URI in the large values area, but record the hash and length here. 268 byte[] hash = computeHash(stringValue); 269 stream.writeChar('L'); 270 stream.writeInt(hash.length); 271 stream.write(hash); 272 stream.writeLong(stringValue.length()); 273 // Now write to the large objects ... 274 largeValues.write(computeHash(stringValue), stringValue.length(), PropertyType.URI, stringValue); 275 } else { 276 stream.writeChar('I'); 277 stream.writeObject(stringValue); 278 } 279 } else if (value instanceof Name) { 280 stream.writeChar('N'); 281 stream.writeObject(((Name)value).getString()); 282 } else if (value instanceof Path) { 283 stream.writeChar('P'); 284 stream.writeObject(((Path)value).getString()); 285 } else if (value instanceof DateTime) { 286 stream.writeChar('T'); 287 stream.writeObject(((DateTime)value).getString()); 288 } else if (value instanceof BigDecimal) { 289 stream.writeChar('D'); 290 stream.writeObject(value); 291 } else if (value instanceof Character) { 292 stream.writeChar('c'); 293 char c = ((Character)value).charValue(); 294 stream.writeChar(c); 295 } else if (value instanceof Reference) { 296 stream.writeChar('R'); 297 Reference ref = (Reference)value; 298 stream.writeObject(ref.getString()); 299 references.write(ref); 300 } else if (value instanceof Binary) { 301 Binary binary = (Binary)value; 302 byte[] hash = null; 303 long length = 0; 304 try { 305 binary.acquire(); 306 length = binary.getSize(); 307 if (largeValues != null && length > largeValues.getMinimumSize()) { 308 // Store the value in the large values area, but record the hash and length here. 309 hash = binary.getHash(); 310 stream.writeChar('L'); 311 stream.writeInt(hash.length); 312 stream.write(hash); 313 stream.writeLong(length); 314 // Write to large objects after releasing the binary 315 } else { 316 // The value is small enough to store here ... 317 stream.writeChar('B'); 318 stream.writeLong(length); 319 InputStream data = binary.getStream(); 320 try { 321 byte[] buffer = new byte[1024]; 322 int numRead = 0; 323 while ((numRead = data.read(buffer)) > -1) { 324 stream.write(buffer, 0, numRead); 325 } 326 } finally { 327 data.close(); 328 } 329 } 330 } finally { 331 binary.release(); 332 } 333 // If this is a large value and the binary has been released, write it to the large objects ... 334 if (largeValues != null && hash != null) { 335 largeValues.write(hash, length, PropertyType.BINARY, value); 336 } 337 } else { 338 // Other kinds of values ... 339 stream.writeChar('O'); 340 stream.writeObject(value); 341 } 342 } 343 stream.flush(); 344 return true; 345 } 346 347 /** 348 * Deserialize the existing properties from the supplied input stream, update the properties, and then serialize the updated 349 * properties to the output stream. 350 * 351 * @param input the stream from which the existing properties are to be deserialized; may not be null 352 * @param output the stream to which the updated properties are to be serialized; may not be null 353 * @param updatedProperties the properties that are being updated (or removed, if there are no values); may not be null 354 * @param largeValues the interface to use for writing large values; may not be null 355 * @param removedLargeValues the interface to use for recording the large values that were removed; may not be null 356 * @param references the interface to use for recording which {@link Reference} values were found during serialization, or 357 * null if the references do not need to be accumulated 358 * @return the number of properties 359 * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code> 360 * @throws ClassNotFoundException if the class for the value's object could not be found 361 */ 362 public int reserializeProperties( ObjectInputStream input, 363 ObjectOutputStream output, 364 Map<Name, Property> updatedProperties, 365 LargeValues largeValues, 366 LargeValues removedLargeValues, 367 ReferenceValues references ) throws IOException, ClassNotFoundException { 368 assert input != null; 369 assert output != null; 370 assert updatedProperties != null; 371 assert largeValues != null; 372 assert references != null; 373 // Assemble a set of property names to skip deserializing 374 Map<Name, Property> allProperties = new HashMap<Name, Property>(); 375 376 // Read the number of properties ... 377 int count = input.readInt(); 378 // Deserialize all of the proeprties ... 379 for (int i = 0; i != count; ++i) { 380 // Read the property name ... 381 String nameStr = (String)input.readObject(); 382 Name name = valueFactories.getNameFactory().create(nameStr); 383 assert name != null; 384 if (updatedProperties.containsKey(name)) { 385 // Deserialized, but don't materialize ... 386 deserializePropertyValues(input, name, true, largeValues, removedLargeValues, references); 387 } else { 388 // Now read the property values ... 389 Object[] values = deserializePropertyValues(input, name, false, largeValues, removedLargeValues, references); 390 // Add the property to the collection ... 391 Property property = propertyFactory.create(name, values); 392 assert property != null; 393 allProperties.put(name, property); 394 } 395 } 396 397 // Add all the updated properties ... 398 for (Map.Entry<Name, Property> entry : updatedProperties.entrySet()) { 399 Property updated = entry.getValue(); 400 if (updated == null) { 401 allProperties.remove(entry.getKey()); 402 } else { 403 allProperties.put(updated.getName(), updated); 404 } 405 } 406 407 // Serialize properties ... 408 int numProperties = allProperties.size(); 409 output.writeInt(numProperties); 410 for (Property property : allProperties.values()) { 411 if (property == null) continue; 412 serializeProperty(output, property, largeValues, references); 413 } 414 return numProperties; 415 } 416 417 /** 418 * Deserialize the properties, adjust all {@link Reference} values that point to an "old" UUID to point to the corresponding 419 * "new" UUID, and reserialize the properties. If any reference is to a UUID not in the map, it is left untouched. 420 * <p> 421 * This is an efficient method that (for the most part) reads from the input stream and directly writes to the output stream. 422 * The exception is when a Reference value is read, that Reference is attempted to be remapped to a new Reference and written 423 * in place of the old reference. (Of course, if the Reference is to a UUID that is not in the "old" to "new" map, the old is 424 * written directly.) 425 * </p> 426 * 427 * @param input the stream from which the existing properties are to be deserialized; may not be null 428 * @param output the stream to which the updated properties are to be serialized; may not be null 429 * @param oldUuidToNewUuid the map of old-to-new UUIDs 430 * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code> 431 * @throws ClassNotFoundException if the class for the value's object could not be found 432 */ 433 public void adjustReferenceProperties( ObjectInputStream input, 434 ObjectOutputStream output, 435 Map<String, String> oldUuidToNewUuid ) throws IOException, ClassNotFoundException { 436 assert input != null; 437 assert output != null; 438 if (oldUuidToNewUuid == null) oldUuidToNewUuid = Collections.emptyMap(); 439 440 UuidFactory uuidFactory = valueFactories.getUuidFactory(); 441 ValueFactory<Reference> referenceFactory = valueFactories.getReferenceFactory(); 442 443 // Read the number of properties ... 444 int count = input.readInt(); 445 output.writeInt(count); 446 // Deserialize all of the proeprties ... 447 for (int i = 0; i != count; ++i) { 448 // Read and write the property name ... 449 Object name = input.readObject(); 450 output.writeObject(name); 451 // Read and write the number of values ... 452 int numValues = input.readInt(); 453 output.writeInt(numValues); 454 // Now read and write each property value ... 455 for (int j = 0; j != numValues; ++j) { 456 // Read and write the type of value ... 457 char type = input.readChar(); 458 output.writeChar(type); 459 switch (type) { 460 case 'S': 461 output.writeObject(input.readObject()); 462 break; 463 case 'b': 464 output.writeBoolean(input.readBoolean()); 465 break; 466 case 'i': 467 output.writeInt(input.readInt()); 468 break; 469 case 'l': 470 output.writeLong(input.readLong()); 471 break; 472 case 's': 473 output.writeShort(input.readShort()); 474 break; 475 case 'f': 476 output.writeFloat(input.readFloat()); 477 break; 478 case 'd': 479 output.writeDouble(input.readDouble()); 480 break; 481 case 'c': 482 // char 483 output.writeChar(input.readChar()); 484 break; 485 case 'U': 486 // UUID 487 output.writeLong(input.readLong()); 488 output.writeLong(input.readLong()); 489 break; 490 case 'I': 491 // URI 492 output.writeObject(input.readObject()); 493 break; 494 case 'N': 495 // Name 496 output.writeObject(input.readObject()); 497 break; 498 case 'P': 499 // Path 500 output.writeObject(input.readObject()); 501 break; 502 case 'T': 503 // DateTime 504 output.writeObject(input.readObject()); 505 break; 506 case 'D': 507 // BigDecimal 508 output.writeObject(input.readObject()); 509 break; 510 case 'R': 511 // Reference 512 String refValue = (String)input.readObject(); 513 Reference ref = referenceFactory.create(refValue); 514 try { 515 UUID toUuid = uuidFactory.create(ref); 516 String newUuid = oldUuidToNewUuid.get(toUuid.toString()); 517 if (newUuid != null) { 518 // Create a new reference ... 519 ref = referenceFactory.create(newUuid); 520 refValue = ref.getString(); 521 } 522 } catch (ValueFormatException e) { 523 // Unknown reference, so simply write it again ... 524 } 525 // Write the reference ... 526 output.writeObject(refValue); 527 break; 528 case 'B': 529 // Binary 530 // Read the length of the content ... 531 long binaryLength = input.readLong(); 532 byte[] content = new byte[(int)binaryLength]; 533 input.read(content); 534 // Now write out the value ... 535 output.writeLong(binaryLength); 536 output.write(content); 537 break; 538 case 'L': 539 // Large object ... 540 int hashLength = input.readInt(); 541 byte[] hash = new byte[hashLength]; 542 input.read(hash); 543 long length = input.readLong(); 544 // write to the output ... 545 output.writeInt(hash.length); 546 output.write(hash); 547 output.writeLong(length); 548 break; 549 default: 550 // All other objects ... 551 output.writeObject(input.readObject()); 552 break; 553 } 554 } 555 } 556 } 557 558 /** 559 * Deserialize the serialized properties on the supplied object stream. 560 * 561 * @param stream the stream that contains the serialized properties; may not be null 562 * @param properties the collection into which each deserialized property is to be placed; may not be null 563 * @param largeValues the interface to use for writing large values; may not be null 564 * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code> 565 * @throws ClassNotFoundException if the class for the value's object could not be found 566 * @see #deserializePropertyValues(ObjectInputStream, Name, boolean, LargeValues, LargeValues, ReferenceValues) 567 * @see #serializeProperties(ObjectOutputStream, int, Iterable, LargeValues, ReferenceValues) 568 */ 569 public void deserializeAllProperties( ObjectInputStream stream, 570 Collection<Property> properties, 571 LargeValues largeValues ) throws IOException, ClassNotFoundException { 572 assert stream != null; 573 assert properties != null; 574 // Read the number of properties ... 575 int count = stream.readInt(); 576 for (int i = 0; i != count; ++i) { 577 Property property = deserializeProperty(stream, largeValues); 578 assert property != null; 579 properties.add(property); 580 } 581 } 582 583 /** 584 * Deserialize the serialized properties on the supplied object stream. 585 * 586 * @param stream the stream that contains the serialized properties; may not be null 587 * @param properties the collection into which each deserialized property is to be placed; may not be null 588 * @param names the names of the properties that should be deserialized; should not be null or empty 589 * @param largeValues the interface to use for writing large values; may not be null 590 * @param skippedLargeValues the interface to use for recording the large values that were skipped; may not be null 591 * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code> 592 * @throws ClassNotFoundException if the class for the value's object could not be found 593 * @see #deserializePropertyValues(ObjectInputStream, Name, boolean, LargeValues, LargeValues, ReferenceValues) 594 * @see #serializeProperties(ObjectOutputStream, int, Iterable, LargeValues, ReferenceValues) 595 */ 596 public void deserializeSomeProperties( ObjectInputStream stream, 597 Collection<Property> properties, 598 LargeValues largeValues, 599 LargeValues skippedLargeValues, 600 Name... names ) throws IOException, ClassNotFoundException { 601 assert stream != null; 602 assert properties != null; 603 assert names != null; 604 assert names.length > 0; 605 Name nameToRead = null; 606 Set<Name> namesToRead = null; 607 if (names.length == 1) { 608 nameToRead = names[0]; 609 } else { 610 namesToRead = new HashSet<Name>(); 611 for (Name name : names) { 612 if (name != null) namesToRead.add(name); 613 } 614 } 615 616 // Read the number of properties ... 617 boolean read = false; 618 int count = stream.readInt(); 619 620 // Now, read the properties (or skip the ones that we're not supposed to read) ... 621 for (int i = 0; i != count; ++i) { 622 // Read the name ... 623 String nameStr = (String)stream.readObject(); 624 Name name = valueFactories.getNameFactory().create(nameStr); 625 assert name != null; 626 read = name.equals(nameToRead) || (namesToRead != null && namesToRead.contains(namesToRead)); 627 if (read) { 628 // Now read the property values ... 629 Object[] values = deserializePropertyValues(stream, name, false, largeValues, skippedLargeValues, null); 630 // Add the property to the collection ... 631 Property property = propertyFactory.create(name, values); 632 assert property != null; 633 properties.add(property); 634 } else { 635 // Skip the property ... 636 deserializePropertyValues(stream, name, true, largeValues, skippedLargeValues, null); 637 } 638 } 639 } 640 641 /** 642 * Deserialize the serialized property on the supplied object stream. 643 * 644 * @param stream the stream that contains the serialized properties; may not be null 645 * @param largeValues the interface to use for writing large values; may not be null 646 * @return the deserialized property; never null 647 * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code> 648 * @throws ClassNotFoundException if the class for the value's object could not be found 649 * @see #deserializeAllProperties(ObjectInputStream, Collection, LargeValues) 650 * @see #serializeProperty(ObjectOutputStream, Property, LargeValues, ReferenceValues) 651 */ 652 public Property deserializeProperty( ObjectInputStream stream, 653 LargeValues largeValues ) throws IOException, ClassNotFoundException { 654 // Read the name ... 655 String nameStr = (String)stream.readObject(); 656 Name name = valueFactories.getNameFactory().create(nameStr); 657 assert name != null; 658 // Now read the property values ... 659 Object[] values = deserializePropertyValues(stream, name, false, largeValues, largeValues, null); 660 // Add the property to the collection ... 661 return propertyFactory.create(name, values); 662 } 663 664 /** 665 * Deserialize the serialized property on the supplied object stream. 666 * 667 * @param stream the stream that contains the serialized properties; may not be null 668 * @param propertyName the name of the property being deserialized 669 * @param skip true if the values don't need to be read, or false if they are to be read 670 * @param largeValues the interface to use for writing large values; may not be null 671 * @param skippedLargeValues the interface to use for recording the large values that were skipped; may not be null 672 * @param references the interface to use for recording which {@link Reference} values were found (and/or removed) during 673 * deserialization; may not be null 674 * @return the deserialized property values, or an empty list if there are no values 675 * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code> 676 * @throws ClassNotFoundException if the class for the value's object could not be found 677 * @see #deserializeAllProperties(ObjectInputStream, Collection, LargeValues) 678 * @see #serializeProperty(ObjectOutputStream, Property, LargeValues, ReferenceValues) 679 */ 680 public Object[] deserializePropertyValues( ObjectInputStream stream, 681 Name propertyName, 682 boolean skip, 683 LargeValues largeValues, 684 LargeValues skippedLargeValues, 685 ReferenceValues references ) throws IOException, ClassNotFoundException { 686 assert stream != null; 687 assert propertyName != null; 688 assert largeValues != null; 689 assert skippedLargeValues != null; 690 // Read the number of values ... 691 int size = stream.readInt(); 692 Object[] values = skip ? null : new Object[size]; 693 for (int i = 0; i != size; ++i) { 694 Object value = null; 695 // Read the type of value ... 696 char type = stream.readChar(); 697 switch (type) { 698 case 'S': 699 String stringValue = (String)stream.readObject(); 700 if (!skip) value = valueFactories.getStringFactory().create(stringValue); 701 break; 702 case 'b': 703 boolean booleanValue = stream.readBoolean(); 704 if (!skip) value = valueFactories.getBooleanFactory().create(booleanValue); 705 break; 706 case 'i': 707 int intValue = stream.readInt(); 708 if (!skip) value = valueFactories.getLongFactory().create(intValue); 709 break; 710 case 'l': 711 long longValue = stream.readLong(); 712 if (!skip) value = valueFactories.getLongFactory().create(longValue); 713 break; 714 case 's': 715 short shortValue = stream.readShort(); 716 if (!skip) value = valueFactories.getLongFactory().create(shortValue); 717 break; 718 case 'f': 719 float floatValue = stream.readFloat(); 720 if (!skip) value = valueFactories.getDoubleFactory().create(floatValue); 721 break; 722 case 'd': 723 double doubleValue = stream.readDouble(); 724 if (!skip) value = valueFactories.getDoubleFactory().create(doubleValue); 725 break; 726 case 'c': 727 // char 728 String charValue = "" + stream.readChar(); 729 if (!skip) value = valueFactories.getStringFactory().create(charValue); 730 break; 731 case 'U': 732 // UUID 733 long msb = stream.readLong(); 734 long lsb = stream.readLong(); 735 if (!skip) { 736 UUID uuid = new UUID(msb, lsb); 737 value = valueFactories.getUuidFactory().create(uuid); 738 } 739 break; 740 case 'I': 741 // URI 742 String uriStr = (String)stream.readObject(); 743 if (!skip) value = valueFactories.getUriFactory().create(uriStr); 744 break; 745 case 'N': 746 // Name 747 String nameValueStr = (String)stream.readObject(); 748 if (!skip) value = valueFactories.getNameFactory().create(nameValueStr); 749 break; 750 case 'P': 751 // Path 752 String pathStr = (String)stream.readObject(); 753 if (!skip) value = valueFactories.getPathFactory().create(pathStr); 754 break; 755 case 'T': 756 // DateTime 757 String dateTimeStr = (String)stream.readObject(); 758 if (!skip) value = valueFactories.getDateFactory().create(dateTimeStr); 759 break; 760 case 'D': 761 // BigDecimal 762 Object bigDecimal = stream.readObject(); 763 if (!skip) value = valueFactories.getDecimalFactory().create(bigDecimal); 764 break; 765 case 'R': 766 // Reference 767 String refValue = (String)stream.readObject(); 768 Reference ref = valueFactories.getReferenceFactory().create(refValue); 769 if (skip) { 770 if (references != null) references.remove(ref); 771 } else { 772 value = ref; 773 if (references != null) references.read(ref); 774 } 775 break; 776 case 'B': 777 // Binary 778 // Read the length of the content ... 779 long binaryLength = stream.readLong(); 780 byte[] content = new byte[(int)binaryLength]; 781 stream.read(content); 782 if (!skip) { 783 value = valueFactories.getBinaryFactory().create(content); 784 } 785 break; 786 case 'L': 787 // Large object ... 788 // Read the hash ... 789 int hashLength = stream.readInt(); 790 byte[] hash = new byte[hashLength]; 791 stream.read(hash); 792 // Read the length of the content ... 793 long length = stream.readLong(); 794 if (skip) { 795 skippedLargeValues.read(valueFactories, hash, length); 796 } else { 797 BinaryFactory factory = valueFactories.getBinaryFactory(); 798 // Look for an already-loaded Binary value with the same hash ... 799 value = factory.find(hash); 800 if (value == null) { 801 // Didn't find an existing large value, so we have to read the large value ... 802 value = largeValues.read(valueFactories, hash, length); 803 } 804 } 805 break; 806 default: 807 // All other objects ... 808 Object object = stream.readObject(); 809 if (!skip) value = valueFactories.getObjectFactory().create(object); 810 break; 811 } 812 if (value != null) values[i] = value; 813 } 814 return values; 815 } 816 817 public byte[] computeHash( String value ) { 818 try { 819 return SecureHash.getHash(SecureHash.Algorithm.SHA_1, value.getBytes()); 820 } catch (NoSuchAlgorithmException e) { 821 throw new SystemFailureException(e); 822 } 823 } 824 }