//{HEADER
/**
* This class is part of jnex 'Nexirius Application Framework for Java'
*
* Copyright (C) Nexirius GmbH, CH-4450 Sissach, Switzerland (www.nexirius.ch)
*
* <p>This library is free software; you can redistribute it and/or<br>
* modify it under the terms of the GNU Lesser General Public<br>
* License as published by the Free Software Foundation; either<br>
* version 2.1 of the License, or (at your option) any later version.</p>
*
* <p>This library is distributed in the hope that it will be useful,<br>
* but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>
* Lesser General Public License for more details.</p>
*
* <p>You should have received a copy of the GNU Lesser General Public<br>
* License along with this library; if not, write to the Free Software<br>
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>
* </blockquote>
*
* <p>
* Nexirius GmbH, hereby disclaims all copyright interest in<br>
* the library jnex' 'Nexirius Application Framework for Java' written<br>
* by Marcel Baumann.</p>
*/
//}HEADER
package com.nexirius.framework.datamodel.persistence;
import com.nexirius.framework.datamodel.DataModel;
import com.nexirius.framework.datamodel.DataModelEnumeration;
import com.nexirius.framework.datamodel.DataModelVector;
import com.nexirius.util.*;
import java.io.*;
import java.util.Iterator;
/**
* This class defines a persistence manager which stores models to files
*
* @author Marcel Baumann
*/
public class FileSystemPersistence implements PersistenceManager {
public static final String DEFAULT_FILE_EXTENSION = ".pm";
private static FileSystemPersistence instance = null;
String rootDirectory;
PersistenceEnvironmentTable environments = new PersistenceEnvironmentTable();
PersistentModelTable openModels = new PersistentModelTable();
boolean checkMagicNumber = true;
boolean verbose = false;
/**
* Create a new file system based persistence manager (rootDirectory is only relevant
* if relative relative DirectoryEnvironments are used)
*/
private FileSystemPersistence(String rootDirectory) {
if (rootDirectory.endsWith(File.separator)) {
this.rootDirectory = rootDirectory;
} else {
this.rootDirectory = rootDirectory + File.separator;
}
}
public static void init(String rootDirectory) {
instance = new FileSystemPersistence(rootDirectory);
}
public static PersistenceManager instance() {
if (instance == null) {
throw new RuntimeException("Can't call instance before init");
}
return instance;
}
public void setCheckMagicNumber(boolean on) {
checkMagicNumber = on;
}
public PersistenceEnvironment getEnvironment(String name)
throws Exception {
return environments.getEnvironment(name);
}
public void addEnvironment(PersistenceEnvironment e)
throws Exception {
PersistenceEnvironment oldEnvironment = (PersistenceEnvironment) environments.get(e.getName());
if (oldEnvironment != null) {
storeAll();
}
environments.putEnvironment(e);
((DirectoryEnvironment) e).createDirectory(rootDirectory);
}
public boolean isOpen(String environmentName, String identifier) {
return null != getOpenModel(environmentName, identifier);
}
/**
* Get a model from the list of open models
*
* @param environmentName
* @param identifier
* @return null if model is not open
*/
public DataModel getOpenModel(String environmentName, String identifier) {
PersistentModelTableEntry persistentModelTableEntry = openModels.get(environmentName, identifier);
if (persistentModelTableEntry != null) {
return persistentModelTableEntry.getDataModel();
}
return null;
}
public DataModelEnumeration getNotSaved() {
DataModelVector v = new DataModelVector();
Iterator iter = environments.getEnvironments().iterator();
while (iter.hasNext()) {
DirectoryEnvironment de = (DirectoryEnvironment) iter.next();
de.getNotSaved(v);
}
return v.getEnumeration();
}
protected XFile makeXFile(DirectoryEnvironment environment, String identifier) {
String extension = environment.getExtension();
if (extension == null) {
extension = DEFAULT_FILE_EXTENSION;
}
return new XFile(environment.getDirectory(rootDirectory), identifier + extension);
}
protected String getIdentifier(DirectoryEnvironment environment, String filename) {
String extension = environment.getExtension();
if (extension == null) {
extension = DEFAULT_FILE_EXTENSION;
}
return filename.substring(0, filename.length() - extension.length());
}
/**
* If the model is open then it will be saved and closed.
* If the model is not open then create a new file and close the model.
*
* @param model
* @param environmentName
* @throws Exception
*/
public void store(DataModel model, String environmentName)
throws Exception {
synchronized (model) {
p("store", model.getInstanceName(), environmentName);
if (model.getInstanceName() == null) {
throw new Exception("Cannot store model with null instance name");
}
DirectoryEnvironment environment = (DirectoryEnvironment) getEnvironment(environmentName);
PersistentModelTableEntry e = openModels.get(environmentName, model.getInstanceName());
XFile file = makeXFile(environment, model.getInstanceName());
if (e == null) {
// look if the file already existst
if (file.exists()) {
throw new Exception("Can't overwrite existing file '" + file.getAbsolutePath() + "'");
}
e = openModels.put(model, environment);
}
doSave(file, environment, model);
}
}
/**
* @param model
* @param environmentName
* @throws Exception
*/
public void save(DataModel model, String environmentName)
throws Exception {
synchronized (model) {
p("save", model.getInstanceName(), environmentName);
if (model.getInstanceName() == null) {
throw new Exception("Cannot save model with null instance name");
}
DirectoryEnvironment environment = (DirectoryEnvironment) getEnvironment(environmentName);
XFile file = makeXFile(environment, model.getInstanceName());
if (!file.exists()) {
throw new Exception("Can't create file (use store) '" + file.getAbsolutePath() + "'");
}
doSave(file, environment, model);
}
}
private void doSave(XFile file, DirectoryEnvironment environment, DataModel model) throws Exception {
OutputStream out = new FileOutputStream(file);
if (environment.useMagicNumber()) {
out = new MagicNumberOutputStream(out);
}
// write to the file
TextToken className = new TextToken(model.getClass().getName(), TextToken.STRING);
className.writeTo(out);
model.writeDataTo(out);
out.close();
environment.removeNeedSaving(model.getInstanceName());
}
public DataModel load(String environmentName, String identifier)
throws Exception {
p("load", identifier, environmentName);
if (isOpen(environmentName, identifier)) {
return openModels.get(environmentName, identifier).getDataModel();
}
DirectoryEnvironment environment = (DirectoryEnvironment) getEnvironment(environmentName);
DataModel m = readDataModel(environment, identifier, environment.useMagicNumber);
openModels.put(m, environment);
m.addSoftDataModelListener(environment.getChangeListener());
return m;
}
public void remove(String environmentName, String identifier)
throws Exception {
p("remove", identifier, environmentName);
if (isOpen(environmentName, identifier)) {
throw new Exception("Can't remove '" + identifier + "' from '" + environmentName + "' because it is still open");
}
DirectoryEnvironment environment = (DirectoryEnvironment) getEnvironment(environmentName);
XFile file = makeXFile(environment, identifier);
if (!file.exists()) {
throw new Exception("Can't remove '" + identifier + "' file not found: " + file.getAbsolutePath());
}
String directory = environment.getDirectory(this.rootDirectory);
XFile dir = new XFile(directory, "deleted");
if (!dir.exists()) {
dir.mkdir();
}
if (dir.isDirectory()) {
XFile target = new XFile(dir, file.getName());
target.createFrom(file, false);
}
if (!file.delete()) {
throw new Exception("Can't remove '" + identifier + "' file: " + file.getAbsolutePath());
}
environment.removeNeedSaving(identifier);
}
public void close(String environmentName, DataModel model, boolean looseChanges)
throws Exception {
p("close", model.getInstanceName(), "-");
DirectoryEnvironment de = (DirectoryEnvironment) environments.getEnvironment(environmentName);
String identifier = model.getInstanceName();
PersistentModelTableEntry e = openModels.get(environmentName, identifier);
if (e == null) {
throw new Exception("Can't close '" + environmentName + '.' + identifier + "' it is not open");
}
if (!looseChanges) {
synchronized (e.model) {
if (de.needSaving(identifier)) {
throw new Exception("Can't close '" + environmentName + identifier + "' because it needs saving");
}
}
}
openModels.remove(environmentName, identifier);
de.removeNeedSaving(identifier);
}
public void storeAll() throws Exception {
p("storeAll", "", "");
Iterator iter = openModels.getAllEntries().iterator();
while (iter.hasNext()) {
PersistentModelTableEntry entry = (PersistentModelTableEntry) iter.next();
store(entry.getDataModel(), entry.getEnvironment().getName());
}
}
public DataModelVector loadAll(String environmentName)
throws Exception {
p("loadAll", "", environmentName);
DirectoryEnvironment de = (DirectoryEnvironment) getEnvironment(environmentName);
StringVector files = de.getFiles(rootDirectory, true);
DataModelVector v = new DataModelVector();
for (String f = files.firstItem(); f != null; f = files.nextItem()) {
try {
String identifier = getIdentifier(de, f);
DataModel m = readDataModel(de, identifier, de.useMagicNumber());
if (m != null) {
v.append(m);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
return v;
}
private DataModel readDataModel(DirectoryEnvironment environment, String identifier, boolean useMagicNumber)
throws Exception {
p("readDataModel", identifier, environment.getName());
DataModel m = null;
XFile file = makeXFile(environment, identifier);
if (!file.exists()) {
throw new Exception("Can't load '" + identifier + "' file not found: " + file.getAbsolutePath());
}
BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream(file));
PushbackInputStream in = null;
if (useMagicNumber) {
in = new MagicNumberInputStream(fileIn);
} else {
in = new PushbackInputStream(fileIn);
}
TextToken className = TextToken.nextToken(in);
if (className.isString()) {
Class cl = Class.forName(className.getString());
m = (DataModel) cl.newInstance();
m.readDataFrom(in);
} else {
throw new Exception("Expecting String literal (class name) at beginning of file but have: " + className);
}
if (useMagicNumber) {
MagicNumberInputStream ms = (MagicNumberInputStream) in;
while (!ms.hasMagic()) {
if (-1 == ms.read()) {
// no magic number found
break;
}
}
if (!ms.checkMagic()) {
in.close();
throw new MagicNumberException("Wrong magic number in: " + file.getAbsolutePath());
}
}
in.close();
m.setInstanceName(identifier);
return m;
}
public StringVector getEnvironments() {
return environments.getEnvironmentNames();
}
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
private void p(String method, String model, String env) {
if (verbose) {
System.out.println(method + " " + model + " " + env);
DataModelEnumeration e = openModels.getEnumeration();
System.out.print("open[");
while (e.hasMore()) {
System.out.print(e.next().getInstanceName() + " ");
}
System.out.println("]");
StringVector envs = getEnvironments();
for (env = envs.firstItem(); env != null; env = envs.nextItem()) {
try {
System.out.print(env + ": ");
((DirectoryEnvironment) getEnvironment(env)).p();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
}