package org.codefx.libfx.serialization; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Optional; import java.util.Random; /** * Demonstrates how to use the {@link SerializableOptional}. */ @SuppressWarnings("static-method") public class SerializableOptionalDemo { /** * Runs this demo. * * @param args * command line arguments (will not be used) * @throws Exception * if (de)serialization fails */ public static void main(String[] args) throws Exception { SerializableOptionalDemo demo = new SerializableOptionalDemo(); demo.serializeString(); demo.failSerializingOptional(); demo.serializeEmptySerializableOptional(); demo.serializeNonEmptySerializableOptional(); print(""); demo.callMethodsWithSerializableOptional(); } // DEMO // serialize "simple" objects, i.e. ones which contain no further instances, to demo serialization in general /** * To get started, serialize a string, deserialize it and print its value. * * @throws Exception * if (de)serialization fails */ private void serializeString() throws Exception { String someString = "a string"; String deserializedString = serializeAndDeserialize(someString); print("The deserialized 'String' is \"" + deserializedString + "\"."); } /** * Try the same with an {@code Optional<String>}, which will fail as {@link Optional} is not {@link Serializable}. * * @throws Exception * if (de)serialization fails */ private void failSerializingOptional() throws Exception { try { Optional<String> someOptional = Optional.of("another string"); Optional<String> deserializedOptional = serializeAndDeserialize(someOptional); print("The deserialized 'Optional' should have the value \"" + deserializedOptional.get() + "\"."); } catch (NotSerializableException e) { print("Serialization of 'Optional' failed as expected."); } } /** * Create a {@link SerializableOptional} from an empty {@link Optional} and (de)serialize it successfully. * * @throws Exception * if (de)serialization fails */ private void serializeEmptySerializableOptional() throws Exception { Optional<String> someOptional = Optional.empty(); SerializableOptional<String> serializableOptional = SerializableOptional.fromOptional(someOptional); Optional<String> deserializedOptional = serializeAndDeserialize(serializableOptional).asOptional(); print("The deserialized empty 'SerializableOptional' has no value: " + !deserializedOptional.isPresent() + "."); } /** * Create a {@link SerializableOptional} from a nonempty {@link Optional} and (de)serialize it successfully. * * @throws Exception * if (de)serialization fails */ private void serializeNonEmptySerializableOptional() throws Exception { Optional<String> someOptional = Optional.of("another string"); SerializableOptional<String> serializableOptional = SerializableOptional.fromOptional(someOptional); Optional<String> deserializedOptional = serializeAndDeserialize(serializableOptional).asOptional(); print("The deserialized non-empty 'SerializableOptional' has the value \"" + deserializedOptional.get() + "\"."); } // use 'SerializableOptional' in method signatures /** * Shows how to quickly wrap and unwrap an {@link Optional} for RPC method calls which rely on serialization. * <p> * Note that {@link SearchAndLog}'s methods have {@link SerializableOptional} as argument and return type. */ private void callMethodsWithSerializableOptional() { SearchAndLog searchAndLog = new SearchAndLog(); for (int id = 0; id < 7; id++) { // unwrap the returned optional using 'asOptional' Optional<String> searchResult = searchAndLog.search(id).asOptional(); // wrap the optional using 'fromOptional'; if used often, this could be a static import searchAndLog.log(id, SerializableOptional.fromOptional(searchResult)); } } // USABILITY /** * Serializes the specified instance to disk. Then deserializes the file and returns the deserialized value. * * @param <T> * the type of the serialized instance * @param serialized * the instance to be serialized * @return the deserialized instance * @throws Exception * if (de)serialization fails */ private static <T> T serializeAndDeserialize(T serialized) throws Exception { File serializeFile = new File("_serialized"); // serialize try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(serializeFile))) { out.writeObject(serialized); } // deserialize try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(serializeFile))) { @SuppressWarnings("unchecked") T deserialized = (T) in.readObject(); return deserialized; } } /** * Prints the specified text to the console. * * @param text * the text to print */ private static void print(String text) { System.out.println(text); } // INNER CLASS FOR METHOD CALLS /** * A class with methods which have an optional return value or argument. */ private static class SearchAndLog { Random random = new Random(); public SerializableOptional<String> search(@SuppressWarnings("unused") int id) { boolean searchSuccessfull = random.nextBoolean(); if (searchSuccessfull) return SerializableOptional.of("found something!"); else return SerializableOptional.empty(); } public void log(int id, SerializableOptional<String> item) { print("Search result for id " + id + ": " + item.asOptional().orElse("empty search result")); } } }