package org.prevayler; import org.prevayler.foundation.serialization.JavaSerializer; import org.prevayler.foundation.serialization.Serializer; import org.prevayler.foundation.serialization.SkaringaSerializer; import org.prevayler.foundation.serialization.XStreamSerializer; import org.prevayler.implementation.PrevaylerDirectory; import org.prevayler.implementation.PrevaylerImpl; import org.prevayler.implementation.clock.MachineClock; import org.prevayler.implementation.journal.Journal; import org.prevayler.implementation.journal.PersistentJournal; import org.prevayler.implementation.journal.TransientJournal; import org.prevayler.implementation.publishing.CentralPublisher; import org.prevayler.implementation.publishing.TransactionPublisher; import java.io.IOException; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Provides easy access to all Prevayler configurations and implementations * available in this distribution. Static methods are also provided as * short-cuts for the most common configurations. <br> * By default, the Prevayler instances created by this class will write their * Transactions to .journal files before executing them. The * FileDescriptor.sync() method is called to make sure the Java file * write-buffers have been written to the operating system. Many operating * systems, including most recent versions of Linux and Windows, allow the * hard-drive's write-cache to be disabled. This guarantees no executed * Transaction will be lost in the event of a power shortage, for example. <br> * Also by default, the Prevayler instances created by this class will filter * out all Transactions that would throw a RuntimeException or Error if executed * on the Prevalent System. This requires enough RAM to hold another copy of the * prevalent system. * @see Prevayler */ public class PrevaylerFactory { private Object _prevalentSystem; private Clock _clock; private boolean _transactionFiltering=true; private String _prevalenceDirectory; private long _journalSizeThreshold; private long _journalAgeThreshold; private Serializer _journalSerializer; private String _journalSuffix; private Map _snapshotSerializers=new HashMap(); private String _primarySnapshotSuffix; /** * Creates a Prevayler that will use a directory called "PrevalenceBase" * under the current directory to read and write its .snapshot and .journal * files. * @param newPrevalentSystemThe newly started, "empty" prevalent system that will be used * as a starting point for every system startup, until the first * snapshot is taken. */ public static Prevayler createPrevayler( Serializable newPrevalentSystem) throws IOException, ClassNotFoundException { return createPrevayler(newPrevalentSystem,"PrevalenceBase"); } /** * Creates a Prevayler that will use the given prevalenceBase directory to * read and write its .snapshot and .journal files. * @param newPrevalentSystemThe newly started, "empty" prevalent system that will be used * as a starting point for every system startup, until the first * snapshot is taken. * @param prevalenceBaseThe directory where the .snapshot files and .journal files * will be read and written. */ public static Prevayler createPrevayler( Serializable newPrevalentSystem, String prevalenceBase) throws IOException, ClassNotFoundException { PrevaylerFactory factory=new PrevaylerFactory(); factory.configurePrevalentSystem(newPrevalentSystem); factory.configurePrevalenceDirectory(prevalenceBase); return factory.create(); } /** * Creates a Prevayler that will execute Transactions WITHOUT writing them * to disk. This is useful for running automated tests or demos MUCH faster * than with a regular Prevayler. * Attempts to take snapshots on this transient Prevayler will throw an * IOException. * @param newPrevalentSystemThe newly started, "empty" prevalent system. * @see #createCheckpointPrevayler(Serializable newPrevalentSystem,String snapshotDirectory) */ public static Prevayler createTransientPrevayler( Serializable newPrevalentSystem){ PrevaylerFactory factory=new PrevaylerFactory(); factory.configurePrevalentSystem(newPrevalentSystem); hook69(newPrevalentSystem,factory); try { return factory.create(); } catch ( Exception e) { e.printStackTrace(); return null; } } /** * @deprecated Use createCheckpointPrevayler() instead of this method. * Deprecated since Prevayler2.00.001. */ public static Prevayler createTransientPrevayler( Serializable newPrevalentSystem, String snapshotDirectory){ return createCheckpointPrevayler(newPrevalentSystem,snapshotDirectory); } /** * Creates a Prevayler that will execute Transactions WITHOUT writing them * to disk. Snapshots will work as "checkpoints" for the system, therefore. * This is useful for stand-alone applications that have a "Save" button, * for example. * @param newPrevalentSystemThe newly started, "empty" prevalent system that will be used * as a starting point for every system startup, until the first * snapshot is taken. * @param snapshotDirectoryThe directory where the .snapshot files will be read and * written. */ public static Prevayler createCheckpointPrevayler( Serializable newPrevalentSystem, String snapshotDirectory){ PrevaylerFactory factory=new PrevaylerFactory(); factory.configurePrevalentSystem(newPrevalentSystem); factory.configurePrevalenceDirectory(snapshotDirectory); hook70(factory); try { return factory.create(); } catch ( Exception e) { e.printStackTrace(); return null; } } private Clock clock(){ return _clock != null ? _clock : new MachineClock(); } /** * Configures the Clock that will be used by the created Prevayler. The * Clock interface can be implemented by the application if it requires * Prevayler to use a special time source other than the machine clock * (default). */ public void configureClock( Clock clock){ _clock=clock; } /** * Configures the directory where the created Prevayler will read and write * its .journal and .snapshot files. The default is a directory called * "PrevalenceBase" under the current directory. * @param prevalenceDirectoryWill be ignored for the .snapshot files if a SnapshotManager * is configured. */ public void configurePrevalenceDirectory( String prevalenceDirectory){ _prevalenceDirectory=prevalenceDirectory; } /** * Configures the prevalent system that will be used by the Prevayler * created by this factory. * @param newPrevalentSystemIf the default Serializer is used, this prevalentSystem must * be Serializable. If another Serializer is used, this * prevalentSystem must be compatible with it. * @see #configureSnapshotSerializer(String,Serializer) */ public void configurePrevalentSystem( Object newPrevalentSystem){ _prevalentSystem=newPrevalentSystem; } /** * Determines whether the Prevayler created by this factory should filter * out all Transactions that would throw a RuntimeException or Error if * executed on the Prevalent System (default is true). This requires enough * RAM to hold another copy of the prevalent system. */ public void configureTransactionFiltering( boolean transactionFiltering){ _transactionFiltering=transactionFiltering; } /** * Configures the size (in bytes) of the journal file. When the current * journal exceeds this size, a new journal is created. */ public void configureJournalFileSizeThreshold( long sizeInBytes){ _journalSizeThreshold=sizeInBytes; } /** * Sets the age (in milliseconds) of the journal file. When the current * journal expires, a new journal is created. */ public void configureJournalFileAgeThreshold( long ageInMilliseconds){ _journalAgeThreshold=ageInMilliseconds; } public void configureJournalSerializer( JavaSerializer serializer){ configureJournalSerializer("journal",serializer); } public void configureJournalSerializer( XStreamSerializer serializer){ configureJournalSerializer("xstreamjournal",serializer); } public void configureJournalSerializer( SkaringaSerializer serializer){ configureJournalSerializer("skaringajournal",serializer); } /** * Configures the transaction journal Serializer to be used by the Prevayler * created by this factory. Only one Serializer is supported at a time. If * you want to change the Serializer of a system in production, you will * have to take a snapshot first because the journal files written by the * previous Serializer will not be read. */ public void configureJournalSerializer( String suffix, Serializer serializer){ PrevaylerDirectory.checkValidJournalSuffix(suffix); if (_journalSerializer != null) { throw new IllegalStateException("Read the javadoc to this method."); } _journalSerializer=serializer; _journalSuffix=suffix; } /** * Returns a Prevayler created according to what was defined by calls to the * configuration methods above. * @throws IOExceptionIf there is trouble creating the Prevalence Base directory or * reading a .journal or .snapshot file. * @throws ClassNotFoundExceptionIf a class of a serialized Object is not found when reading a * .journal or .snapshot file. */ public Prevayler create() throws IOException, ClassNotFoundException { return new PrevaylerFactory_create(this).execute(); } private String prevalenceDirectory(){ return _prevalenceDirectory != null ? _prevalenceDirectory : "Prevalence"; } private Object prevalentSystem(){ if (_prevalentSystem == null) throw new IllegalStateException("The prevalent system must be configured."); return _prevalentSystem; } private TransactionPublisher publisher() throws IOException { try { this.hook67(); return new CentralPublisher(clock(),journal()); } catch ( ReturnObject r) { return (TransactionPublisher)r.value; } } private Journal journal() throws IOException { return new PrevaylerFactory_journal(this).execute(); } private Serializer journalSerializer(){ if (_journalSerializer != null) return _journalSerializer; return new JavaSerializer(); } private String journalSuffix(){ return _journalSuffix != null ? _journalSuffix : "journal"; } @MethodObject static class PrevaylerFactory_create { PrevaylerFactory_create( PrevaylerFactory _this){ this._this=_this; } Prevayler execute() throws IOException, ClassNotFoundException { try { this.hook73(); { } this.hook72(); this.hook68(); this.hook71(); throw ReturnHack.returnObject; } catch ( ReturnObject r) { return (Prevayler)r.value; } } protected PrevaylerFactory _this; protected GenericSnapshotManager snapshotManager; protected TransactionPublisher publisher; protected void hook68() throws IOException, ClassNotFoundException { } protected void hook71() throws IOException, ClassNotFoundException { } protected void hook72() throws IOException, ClassNotFoundException { } protected void hook73() throws IOException, ClassNotFoundException { } } @MethodObject static class PrevaylerFactory_journal { PrevaylerFactory_journal( PrevaylerFactory _this){ this._this=_this; } Journal execute() throws IOException { try { this.hook74(); throw ReturnHack.returnObject; } catch ( ReturnObject r) { return (Journal)r.value; } } protected PrevaylerFactory _this; protected PrevaylerDirectory directory; protected void hook74() throws IOException { throw new ReturnObject((Journal)new TransientJournal()); } } protected void hook67() throws IOException { } protected static void hook69( Serializable newPrevalentSystem, PrevaylerFactory factory){ } protected static void hook70( PrevaylerFactory factory){ } }