00001 /******************************************************************************* 00002 00003 @file Pickle.d 00004 00005 This is a distillation of the relevant Mango.io portions, combined 00006 for the purposes of readability. The original files include: 00007 00008 IWriter.d 00009 IReader.d 00010 IPickle.d 00011 PickleRegistry.d 00012 Exception.d 00013 00014 Copyright (C) 2004 Kris Bell 00015 00016 This software is provided 'as-is', without any express or implied 00017 warranty. In no event will the authors be held liable for damages 00018 of any kind arising from the use of this software. 00019 00020 Permission is hereby granted to anyone to use this software for any 00021 purpose, including commercial applications, and to alter it and/or 00022 redistribute it freely, subject to the following restrictions: 00023 00024 1. The origin of this software must not be misrepresented; you must 00025 not claim that you wrote the original software. If you use this 00026 software in a product, an acknowledgment within documentation of 00027 said product would be appreciated but is not required. 00028 00029 2. Altered source versions must be plainly marked as such, and must 00030 not be misrepresented as being the original software. 00031 00032 3. This notice may not be removed or altered from any distribution 00033 of the source. 00034 00035 00036 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00037 00038 00039 @version Initial version, November 2004 00040 @author Kris 00041 00042 *******************************************************************************/ 00043 00044 00045 00046 /******************************************************************************* 00047 00048 Any class implementing IReadable becomes part of the Reader framework 00049 00050 *******************************************************************************/ 00051 00052 interface IReadable 00053 { 00054 void read (IReader r); 00055 } 00056 00057 /******************************************************************************* 00058 00059 Interface to make any class compatible with any IWriter. 00060 00061 *******************************************************************************/ 00062 00063 interface IWritable 00064 { 00065 void write (IWriter w); 00066 } 00067 00068 /******************************************************************************* 00069 00070 Interface for all serializable classes. Such classes are intended 00071 to be transported over a network, or be frozen in a file for later 00072 reconstruction. 00073 00074 *******************************************************************************/ 00075 00076 interface IPickle : IWritable, IReadable 00077 { 00078 /*********************************************************************** 00079 00080 Identify this serializable class via a char[]. This should 00081 be (per class) unique within the domain. Use version numbers 00082 or similar mechanism to isolate different implementations of 00083 the same class. 00084 00085 ***********************************************************************/ 00086 00087 char[] getGuid (); 00088 } 00089 00090 00091 /******************************************************************************* 00092 00093 Interface for all deserializable classes. Such classes either 00094 implement the full concrete class instance or they act as a 00095 proxy of sorts, creating the true instance only when called 00096 upon to do so. An IPickleProxy could perhaps take alternative 00097 action when called upon to create an "old" or "unsupported" 00098 class guid. The default behaviour is to throw an exception 00099 when an unknown guid is seen. 00100 00101 *******************************************************************************/ 00102 00103 interface IPickleFactory 00104 { 00105 /*********************************************************************** 00106 00107 Identify this serializable class via a char[]. This should 00108 be (per class) unique within the domain. Use version numbers 00109 or similar mechanism to isolate different implementations of 00110 the same class. 00111 00112 ***********************************************************************/ 00113 00114 char[] getGuid (); 00115 00116 /*********************************************************************** 00117 00118 This defines the factory method. Each IPickleProxy object 00119 provides a factory for creating a deserialized instance. 00120 The factory is registered along with the appropriate guid. 00121 00122 ***********************************************************************/ 00123 00124 Object create (IReader reader); 00125 } 00126 00127 00128 /******************************************************************************* 00129 00130 PickleException is thrown when the PickleRegistry encounters a 00131 problem during proxy registration, or when it sees an unregistered 00132 guid. 00133 00134 *******************************************************************************/ 00135 00136 class PickleException : IOException 00137 { 00138 /*********************************************************************** 00139 00140 Construct exception with the provided text string 00141 00142 ***********************************************************************/ 00143 00144 this (char[] msg) 00145 { 00146 super (msg); 00147 } 00148 } 00149 00150 /******************************************************************************* 00151 00152 Bare framework for registering and creating serializable objects. 00153 Such objects are intended to be transported across a local network 00154 and re-instantiated at some destination node. 00155 00156 Each IPickle exposes the means to write, or freeze, its content. An 00157 IPickleFactory provides the means to create a new instance of itself 00158 populated with thawed data. Frozen objects are uniquely identified 00159 by a guid exposed via the interface. Responsibility of maintaining 00160 uniqueness across said identifiers lies in the hands of the developer. 00161 00162 See PickleReader for an example of how this is expected to operate. 00163 00164 *******************************************************************************/ 00165 00166 class PickleRegistry 00167 { 00168 private static Proxy[char[]] registry; 00169 00170 /*********************************************************************** 00171 00172 This is a singleton: the constructor should not be exposed 00173 00174 ***********************************************************************/ 00175 00176 private this () 00177 { 00178 } 00179 00180 /*********************************************************************** 00181 00182 IPickleFactory registrar entry 00183 00184 ***********************************************************************/ 00185 00186 private class Proxy 00187 { 00188 IPickleFactory factory; 00189 00190 /*************************************************************** 00191 00192 ***************************************************************/ 00193 00194 this (IPickleFactory factory) 00195 { 00196 this.factory = factory; 00197 } 00198 00199 /*************************************************************** 00200 00201 ***************************************************************/ 00202 00203 Object create (IReader reader) 00204 { 00205 return factory.create (reader); 00206 } 00207 } 00208 00209 /*********************************************************************** 00210 00211 Add the provided object to the registry. Note that one 00212 cannot change a registration once it is placed. Neither 00213 can one remove registered item. This is done to avoid 00214 major issues when trying to synchronize servers across 00215 a farm, which may still have live instances of "old" 00216 objects waiting to be passed around the cluster. New 00217 versions of an object should be given a distinct guid 00218 from the prior version; appending an incremental number 00219 may well be sufficient for your needs. 00220 00221 ***********************************************************************/ 00222 00223 static void enroll (IPickleFactory object) 00224 { 00225 char[] guid = object.getGuid; 00226 00227 synchronized { 00228 if (guid in registry) 00229 throw new PickleException ("Invalid attempt to re-register a guid"); 00230 00231 registry[guid] = new Proxy (object); 00232 } 00233 } 00234 00235 /*********************************************************************** 00236 00237 Create a new instance of a registered class from the content 00238 made available via the given reader. The factory is located 00239 using the provided guid, which must match an enrolled class. 00240 00241 ***********************************************************************/ 00242 00243 static Object create (IReader reader, char[] guid) 00244 { 00245 // locate the appropriate Proxy. 00246 synchronized { 00247 if (guid in registry) 00248 return registry[guid].create(reader); 00249 } 00250 00251 throw new PickleException ("Attempt to unpickle via unregistered guid '"~guid~"'"); 00252 } 00253 } 00254