You are viewing a plain text version of this content. The canonical link for it is here.
Posted to slide-dev@jakarta.apache.org by oz...@apache.org on 2004/05/05 14:20:44 UTC
cvs commit: jakarta-slide/src/stores/org/apache/slide/store/txfile/rm/impl FileSequence.java
ozeigermann 2004/05/05 05:20:44
Added: src/stores/org/apache/slide/store/txfile
FileSequenceStore.java
src/stores/org/apache/slide/store/txfile/rm/impl
FileSequence.java
Log:
Added file system sequence implementation
Revision Changes Path
1.1 jakarta-slide/src/stores/org/apache/slide/store/txfile/FileSequenceStore.java
Index: FileSequenceStore.java
===================================================================
/*
* $Header: /home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txfile/FileSequenceStore.java,v 1.1 2004/05/05 12:20:44 ozeigermann Exp $
* $Revision: 1.1 $
* $Date: 2004/05/05 12:20:44 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.slide.store.txfile;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.ServiceConnectionFailedException;
import org.apache.slide.common.ServiceDisconnectionFailedException;
import org.apache.slide.common.ServiceParameterErrorException;
import org.apache.slide.common.ServiceParameterMissingException;
import org.apache.slide.common.ServiceResetFailedException;
import org.apache.slide.common.AbstractServiceBase;
import org.apache.slide.store.SequenceStore;
import org.apache.slide.store.txfile.rm.ResourceManagerException;
import org.apache.slide.store.txfile.rm.impl.FileSequence;
import org.apache.slide.util.logger.Logger;
import org.apache.slide.util.logger.StoreLogger;
/**
* Sequence store using the file system.
*
* @author <a href="mailto:ozeigermann@c1-fse.de">Oliver Zeigermann</a>
*/
public class FileSequenceStore extends AbstractServiceBase implements SequenceStore {
protected static final String LOG_CHANNEL = FileSequenceStore.class.getName();
protected static final String STORE_DIR_PARAMETER = "rootpath";
protected FileSequence fileSequence;
protected String storeDir = null;
protected Map sequenceHash = new HashMap();
protected int sequenceIncrement = 100;
protected long sequenceStartValue = 1;
public void setParameters(Hashtable parameters)
throws ServiceParameterErrorException, ServiceParameterMissingException {
storeDir = (String) parameters.get(STORE_DIR_PARAMETER);
if (storeDir == null) {
throw new ServiceParameterMissingException(this, STORE_DIR_PARAMETER);
}
try {
fileSequence = new FileSequence(storeDir, new StoreLogger(getLogger(), LOG_CHANNEL));
getLogger().log("File Sequence Store configured to " + storeDir, LOG_CHANNEL, Logger.INFO);
} catch (ResourceManagerException e) {
getLogger().log("Can not initialize File Sequence Store", e, LOG_CHANNEL, Logger.CRITICAL);
throw new ServiceParameterErrorException(this, STORE_DIR_PARAMETER);
}
}
public String toString() {
return "FileSequenceStore at " + storeDir;
}
/**
* @see org.apache.slide.common.AbstractServiceBase#connect()
*/
public void connect() throws ServiceConnectionFailedException {
}
/**
* @see org.apache.slide.common.AbstractServiceBase#disconnect()
*/
public void disconnect() throws ServiceDisconnectionFailedException {
}
/**
* @see org.apache.slide.common.AbstractServiceBase#reset()
*/
public void reset() throws ServiceResetFailedException {
}
/**
* @see org.apache.slide.common.AbstractServiceBase#isConnected()
*/
public boolean isConnected() throws ServiceAccessException {
return true;
}
/**
* @see org.apache.slide.store.SequenceStore#isSequenceSupported()
*/
public boolean isSequenceSupported() {
return true;
}
/**
* @see org.apache.slide.store.SequenceStore#sequenceExists(java.lang.String)
*/
public boolean sequenceExists(String sequenceName) throws ServiceAccessException {
SeqContext seq = (SeqContext) sequenceHash.get(sequenceName);
if (seq != null) {
return true;
} else {
return fileSequence.exists(sequenceName);
}
}
/**
* @see org.apache.slide.store.SequenceStore#createSequence(java.lang.String)
*/
public synchronized boolean createSequence(String sequenceName) throws ServiceAccessException {
try {
if (fileSequence.create(sequenceName, sequenceStartValue)) {
sequenceHash.put(sequenceName, new SeqContext(sequenceName, sequenceStartValue, sequenceStartValue));
return true;
} else {
return false;
}
} catch (ResourceManagerException e) {
throw new ServiceAccessException(this, e);
}
}
/**
* @see org.apache.slide.store.SequenceStore#nextSequenceValue(java.lang.String)
*/
public synchronized long nextSequenceValue(String sequenceName) throws ServiceAccessException {
// get chunks of values to save disk access
SeqContext seq = (SeqContext) sequenceHash.get(sequenceName);
if (seq != null && seq.pos < seq.end - 1) {
seq.pos++;
return seq.pos;
} else {
try {
if (seq == null) {
seq = new SeqContext(sequenceName);
}
long pos = fileSequence.nextSequenceValueBottom(sequenceName, sequenceIncrement);
seq.pos = pos;
seq.end = seq.pos + sequenceIncrement;
return seq.pos;
} catch (ResourceManagerException e) {
throw new ServiceAccessException(this, e);
}
}
}
/**
* @see javax.transaction.xa.XAResource#commit(javax.transaction.xa.Xid, boolean)
*/
public void commit(Xid xid, boolean onePhase) throws XAException {
// XXX we are not transactional, this is our trick
}
/**
* @see javax.transaction.xa.XAResource#end(javax.transaction.xa.Xid, int)
*/
public void end(Xid xid, int flags) throws XAException {
// XXX we are not transactional, this is our trick
}
/**
* @see javax.transaction.xa.XAResource#forget(javax.transaction.xa.Xid)
*/
public void forget(Xid xid) throws XAException {
// XXX we are not transactional, this is our trick
}
/**
* @see javax.transaction.xa.XAResource#getTransactionTimeout()
*/
public int getTransactionTimeout() throws XAException {
// XXX we are not transactional, this is our trick
return 0;
}
/**
* @see javax.transaction.xa.XAResource#isSameRM(javax.transaction.xa.XAResource)
*/
public boolean isSameRM(XAResource xaResource) throws XAException {
return (xaResource == this);
}
/**
* @see javax.transaction.xa.XAResource#prepare(javax.transaction.xa.Xid)
*/
public int prepare(Xid xid) throws XAException {
// tell TM we do not need to commit
return XA_RDONLY;
}
/**
* @see javax.transaction.xa.XAResource#recover(int)
*/
public Xid[] recover(int flag) throws XAException {
// XXX we are not transactional, this is our trick
return null;
}
/**
* @see javax.transaction.xa.XAResource#rollback(javax.transaction.xa.Xid)
*/
public void rollback(Xid xid) throws XAException {
// XXX we are not transactional, this is our trick
}
/**
* @see javax.transaction.xa.XAResource#setTransactionTimeout(int)
*/
public boolean setTransactionTimeout(int seconds) throws XAException {
// XXX we are not transactional, this is our trick
return true;
}
/**
* @see javax.transaction.xa.XAResource#start(javax.transaction.xa.Xid, int)
*/
public void start(Xid xid, int flags) throws XAException {
// XXX we are not transactional, this is our trick
}
protected static class SeqContext {
public String name;
public long pos;
public long end;
public SeqContext(String name) {
this(name, -1, -1);
}
public SeqContext(String name, long pos, long end) {
this.name = name;
this.pos = pos;
this.end = end;
}
}
}
1.1 jakarta-slide/src/stores/org/apache/slide/store/txfile/rm/impl/FileSequence.java
Index: FileSequence.java
===================================================================
/*
* $Header: /home/cvs/jakarta-slide/src/stores/org/apache/slide/store/txfile/rm/impl/FileSequence.java,v 1.1 2004/05/05 12:20:44 ozeigermann Exp $
* $Revision: 1.1 $
* $Date: 2004/05/05 12:20:44 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.slide.store.txfile.rm.impl;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import org.apache.slide.store.txfile.rm.*;
import org.apache.slide.store.util.FileHelper;
import org.apache.slide.util.logger.StoreLogger;
/**
* Fail-Safe sequence store implementation using the file system.
*
*
* @author <a href="mailto:ozeigermann@c1-fse.de">Oliver Zeigermann</a>
*/
public class FileSequence {
protected final String storeDir;
protected final StoreLogger logger;
/**
* Creates a new resouce manager operation on the specified directories.
*
* @param storeDir directory where main data should go after commit
* @param workDir directory where transactions store temporary data
*/
public FileSequence(String storeDir, StoreLogger logger) throws ResourceManagerException {
this.storeDir = storeDir;
this.logger = logger;
File file = new File(storeDir);
file.mkdirs();
if (!file.exists()) {
throw new ResourceManagerException("Can not create working directory " + storeDir);
}
}
public synchronized boolean exists(String sequenceName) {
String pathI = getPathI(sequenceName);
String pathII = getPathII(sequenceName);
return (FileHelper.fileExists(pathI) || FileHelper.fileExists(pathII));
}
public synchronized boolean create(String sequenceName, long initialValue) throws ResourceManagerException {
if (exists(sequenceName))
return false;
write(sequenceName, initialValue);
return true;
}
public synchronized boolean delete(String sequenceName) {
if (!exists(sequenceName))
return false;
String pathI = getPathI(sequenceName);
String pathII = getPathII(sequenceName);
// XXX be careful no to use shortcut eval with || might not delete second file
boolean res1 = FileHelper.deleteFile(pathI);
boolean res2 = FileHelper.deleteFile(pathII);
return (res1 || res2);
}
public synchronized long nextSequenceValueBottom(String sequenceName, long increment)
throws ResourceManagerException {
if (!exists(sequenceName)) {
throw new ResourceManagerException("Sequence " + sequenceName + " does not exist");
}
if (increment <= 0) {
throw new IllegalArgumentException("Increment must be greater than 0, was " + increment);
}
long value = read(sequenceName);
long newValue = value + increment;
write(sequenceName, newValue);
return value;
}
protected long read(String sequenceName) throws ResourceManagerException {
String pathI = getPathI(sequenceName);
String pathII = getPathII(sequenceName);
long returnValue = -1;
long valueI = -1;
if (FileHelper.fileExists(pathI)) {
try {
valueI = readFromPath(pathI);
} catch (NumberFormatException e) {
throw new ResourceManagerException("Fatal internal error: Backup sequence value corrupted");
} catch (FileNotFoundException e) {
throw new ResourceManagerException("Fatal internal error: Backup sequence vanished");
} catch (IOException e) {
throw new ResourceManagerException("Fatal internal error: Backup sequence value corrupted");
}
}
long valueII = -1;
if (FileHelper.fileExists(pathII)) {
try {
valueII = readFromPath(pathII);
if (valueII > valueI) {
returnValue = valueII;
} else {
// if it is smaller than previous this *must* be an error as we constantly increment
logger.logWarning("Latest sequence value smaller than previous, reverting to previous");
FileHelper.deleteFile(pathII);
returnValue = valueI;
}
} catch (NumberFormatException e) {
logger.logWarning("Latest sequence value corrupted, reverting to previous");
FileHelper.deleteFile(pathII);
returnValue = valueI;
} catch (FileNotFoundException e) {
logger.logWarning("Can not find latest sequence value, reverting to previous");
FileHelper.deleteFile(pathII);
returnValue = valueI;
} catch (IOException e) {
logger.logWarning("Can not read latest sequence value, reverting to previous");
FileHelper.deleteFile(pathII);
returnValue = valueI;
}
} else {
logger.logWarning("Can not read latest sequence value, reverting to previous");
returnValue = valueI;
}
if (returnValue != -1) {
return returnValue;
} else {
throw new ResourceManagerException("Fatal internal error: Could not compute valid sequence value");
}
}
protected void write(String sequenceName, long value) throws ResourceManagerException {
String pathII = getPathII(sequenceName);
File f2 = new File(pathII);
// by contract when this method is called an f2 exists it must be valid
if (f2.exists()) {
// move previous value to backup position
String pathI = getPathI(sequenceName);
File f1 = new File(pathI);
f1.delete();
if (!f2.renameTo(f1)) {
throw new ResourceManagerException("Fatal internal error: Can not create backup value at" + pathI);
}
}
try {
if (!f2.createNewFile()) {
throw new ResourceManagerException("Fatal internal error: Can not create new value at" + pathII);
}
} catch (IOException e) {
throw new ResourceManagerException("Fatal internal error: Can not create new value at" + pathII, e);
}
writeToPath(pathII, value);
}
protected String getPathI(String sequenceName) {
return storeDir + "/" + sequenceName + "_1.seq";
}
protected String getPathII(String sequenceName) {
return storeDir + "/" + sequenceName + "_2.seq";
}
protected long readFromPath(String path)
throws ResourceManagerException, NumberFormatException, FileNotFoundException, IOException {
File file = new File(path);
BufferedReader reader = null;
try {
InputStream is = new FileInputStream(file);
// we do not care for encoding as we only have numbers
reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String valueString = reader.readLine();
long value = Long.parseLong(valueString);
return value;
} catch (UnsupportedEncodingException e) {
throw new ResourceManagerException("Fatal internal error, encoding UTF-8 unknown");
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
}
}
}
}
protected void writeToPath(String path, long value) throws ResourceManagerException {
File file = new File(path);
BufferedWriter writer = null;
try {
OutputStream os = new FileOutputStream(file);
writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
String valueString = Long.toString(value);
writer.write(valueString);
writer.write('\n');
} catch (FileNotFoundException e) {
throw new ResourceManagerException("Fatal internal error: Can not find sequence at " + path);
} catch (IOException e) {
throw new ResourceManagerException("Fatal internal error: Can not write to sequence at " + path);
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
}
}
}
}
// just for testing
public static void main(String[] args) {
try {
FileSequence seq = new FileSequence("E:/tmp/seq", new StoreLogger() {
public void logWarning(String message) {
System.err.println(message);
}
});
seq.create("olli", 10L);
long value = seq.nextSequenceValueBottom("olli", 100);
value = seq.nextSequenceValueBottom("olli", 1000);
seq.delete("olli");
} catch (ResourceManagerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: slide-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: slide-dev-help@jakarta.apache.org