| Pat.java |
/* PAT: Persistent Applications Toolkit (patsystem.sf.net)
* Copyright (C) 2004, 2005 Tomasz Nazar, nthx at irc dot pl
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Full version of the license is /docs/LICENSE.txt
*/
package org.nthx.pat;
import org.apache.log4j.Logger;
import org.prevayler.Command;
import org.prevayler.PrevalentSystem;
import org.prevayler.implementation.SnapshotPrevayler;
import java.io.IOException;
import java.io.Serializable;
/** PAT: some type of 'facade' to main system features. Interceptors, mixins
* are connected by this class.
* <p>Introduction of it helped me to manage all of the scattered PAT logic.
* <p>This class is singleton, because right now I don't like to have full
* set of static methods.
* <p><b>Note</b>: this object is for internall needs. It's not part of public API.
*
* @version $Id: Pat.java 3725 2005-06-09 23:57:03Z nthx $
* @author nthx@users.sourceforge.net
*/
public class Pat
{
//--- internal Fields -------
private transient static Pat INSTANCE;
private transient static Logger log = Logger.getLogger("pat");
private transient static boolean NO_COMMANDS_MODE =
Boolean.valueOf(System.getProperty("pat.no-commands", "false")).booleanValue();
//--- Fields ----------------
private transient PrevalentSystem patRoot; //root of all objects...
private transient SnapshotPrevayler snapshotPrevayler;
private transient IdentityMapMixin temporaryIdentityMap;
//distinction between recovered system from snapshot or just recovered one
private transient boolean freshRoot = true;
transient static boolean finishedCreatingRoot = false;
//--- Constructors ----------
private Pat()
{
temporaryIdentityMap = new IdentityMapMixin();
if (isInNoCommandsMode())
{
String warning =
" ****************************** " +
" * PAT is in NO-COMMANDS-MODE * " +
" ****************************** ";
System.out.println(warning);
log.warn(warning);
}
}
//--- Getters and Setters ---
boolean isFreshRoot() { return freshRoot; }
public IdentityMap getRootAsIdentityMap()
{
if (null != patRoot)
{
if (null != temporaryIdentityMap && finishedCreatingRoot)
throw new IllegalStateException("Temporary IM should be null");
return (IdentityMap)patRoot;
}
else
{
/* This situation happens when root objects is being instantiated
* with <code>new</code> keyword. In this moment 'snapshotPrevayler'
* doesn't exist, but new objects (inside constructor) are being
* created.
* That's why we create temporary IdentityMap for them, and after
* creating 'snapshotPrevayler' we throw it away (after earlier
* snapshot) or copy values to original pat's root.
*/
return temporaryIdentityMap;
}
}
public static Pat getInstance()
{
if (null == INSTANCE)
INSTANCE = new Pat();
return INSTANCE;
}
public static void unload()
{
log.warn("Unloading PAT instance: " + INSTANCE);
INSTANCE = null;
finishedCreatingRoot = false;
}
Serializable executeCommand(Command command)
throws Throwable
{
return snapshotPrevayler.executeCommand(command);
}
public void updateRoot(PrevalentSystem systemFromSnapshot,
PrevalentSystem newlyCreatedSystem)
{
if (systemFromSnapshot == newlyCreatedSystem)
throw new IllegalStateException("Should differ!");
if (null == systemFromSnapshot)
{
freshRoot = true;
//system was just created, and commandLogs will be read for list moment...
patRoot = newlyCreatedSystem;
getRootAsIdentityMap().setMap(temporaryIdentityMap.getMap());
getRootAsIdentityMap().setUniqueOID(temporaryIdentityMap.getUniqueOID());
temporaryIdentityMap = null;
//put root into the IM
getRootAsIdentityMap().putObject((Identifiable)patRoot);
}
else
{
freshRoot = false;
/* system was recovered from snapshot..
So: do not put there nothing, cause there are data in deserialized
root.
But update this local references for next Interceptors..:
*/
patRoot = systemFromSnapshot;
temporaryIdentityMap = null;
((IdentityMap)newlyCreatedSystem).setMap(null);
((IdentityMap)newlyCreatedSystem).setUniqueOID(-999L);
newlyCreatedSystem = null;
}
}
void setSnapshotPrevayler(SnapshotPrevayler snapshotPrevayler)
{
this.snapshotPrevayler = snapshotPrevayler;
if (patRoot != snapshotPrevayler.system())
throw new IllegalStateException("These two objects should be the same...");
}
void takeSnapshot()
{
try
{
snapshotPrevayler.takeSnapshot();
} catch (IOException e)
{
log.error("Why?", e);
}
}
void finishedCreatingRoot()
{
finishedCreatingRoot = true;
printMemoryStatistics();
}
public boolean afterRootConstructor()
{
return finishedCreatingRoot;
}
public boolean beforeRootConstructor()
{
return !afterRootConstructor();
}
boolean isInNoCommandsMode()
{
return NO_COMMANDS_MODE;
}
private void printMemoryStatistics()
{
String percentage = "" + (float)getRootAsIdentityMap().getSize()*100 / Integer.MAX_VALUE;
if (percentage.length() > 4 && percentage.charAt(2) == '.')
percentage = percentage.substring(0, 4);
else
percentage = "0.00";
log.debug("Objects in IdentityMap: "
+ getRootAsIdentityMap().getSize()
+ " Usage of the map: "
+ percentage + "%");
log.debug("------------------- * * * -----------------------");
}
}