com.almende.eve.state.ConcurrentFileState Maven / Gradle / Ivy
package com.almende.eve.state;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import com.almende.eve.rpc.jsonrpc.jackson.JOM;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @class FileState
*
* A persistent state for an Eve Agent, which stores the data on disk.
* Data is stored in the path provided by the configuration file.
*
* The state provides general information for the agent (about itself,
* the environment, and the system configuration), and the agent can
* store its state in the state. The state extends a standard Java
* Map.
*
* All operations on this FileState are thread-safe. It also provides two
* aditional methods: PutIfNotChanged() and PutAllIfNotChanged().
*
* Usage:
* AgentFactory factory = new AgentFactory(config);
* ConcurrentFileState state = new
* ConcurrentFileState("agentId",".eveagents");
* state.put("key", "value");
* System.out.println(state.get("key")); // "value"
*
* @author jos
* @author ludo
*/
public class ConcurrentFileState extends FileState {
Logger logger = Logger.getLogger("ConcurrentFileState");
protected ConcurrentFileState() {
}
private boolean json = false;
private String filename = null;
private FileChannel channel = null;
private FileLock lock = null;
private InputStream fis = null;
private OutputStream fos = null;
private ObjectMapper om = null;
private static Map locked = new ConcurrentHashMap();
private Map properties = Collections
.synchronizedMap(new HashMap());
public ConcurrentFileState(String agentId, String filename) {
super(agentId);
this.filename = filename;
}
public ConcurrentFileState(String agentId, String filename, boolean json) {
super(agentId);
this.filename = filename;
this.json = json;
om = JOM.getInstance();
om.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
om.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
}
@Override
public void finalize() {
closeFile();
}
@SuppressWarnings("resource")
private void openFile() throws Exception {
synchronized (locked) {
while (locked.containsKey(filename) && locked.get(filename)) {
// logger.warning("Starting to wait for locked! "+filename);
locked.wait();
}
locked.put(filename, true);
File file = new File(this.filename);
if (!file.exists()) {
locked.put(filename, false);
locked.notifyAll();
throw new Exception("Warning: File doesn't exist (anymore):'"
+ this.filename + "'");
}
channel = new RandomAccessFile(file, "rw").getChannel();
// logger.warning("Starting to wait for fileLock! "+filename);
try {
// TODO: add support for shared locks, allowing parallel reading
// operations.
lock = channel.lock();
} catch (Exception e) {
channel.close();
channel = null;
lock = null;
locked.put(filename, false);
locked.notifyAll();
throw new Exception("error, couldn't obtain file lock on:"
+ filename, e);
}
// logger.warning("fileLock set! "+filename);
fis = Channels.newInputStream(channel);
fos = Channels.newOutputStream(channel);
}
}
private void closeFile() {
synchronized (locked) {
if (lock.isValid()) {
try {
lock.release();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
fos.close();
fis.close();
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
channel = null;
fis = null;
fos = null;
lock = null;
locked.put(filename, false);
locked.notifyAll();
// logger.warning("locked released! "+filename);
}
}
/**
* write properties to disk
*
* @return success True if successfully written
* @throws IOException
*/
private void write() throws Exception {
channel.position(0);
if (json) {
om.writeValue(fos, properties);
fos.flush();
} else {
ObjectOutput out = new ObjectOutputStream(fos);
out.writeObject(properties);
out.flush();
}
}
/**
* read properties from disk
*
* @return success True if successfully read
* @throws ClassNotFoundException
* @throws IOException
*/
private void read() throws Exception {
_read(false);
}
@SuppressWarnings("unchecked")
private void _read(boolean retry) throws Exception {
try {
channel.position(0);
properties.clear();
if (json ^ retry) {
properties.putAll(om.readValue(fis, HashMap.class));
} else {
ObjectInput in = new ObjectInputStream(fis);
properties.putAll((Map) in.readObject());
}
} catch (EOFException eof) {
// empty file, new agent?
} catch (StreamCorruptedException sce) {
if (channel.position() != 0) {
if (!retry) {
_read(true);
} else {
throw sce;
}
}
// empty file, new agent?
} catch (JsonMappingException map) {
if (channel.position() != 0) {
if (!retry) {
_read(true);
} else {
throw map;
}
}
// empty file, new agent?
}
}
/**
* init is executed once before the agent method is invoked
*/
@Override
public void init() {
}
/**
* destroy is executed once after the agent method is invoked if the
* properties are changed, they will be saved
*/
@Override
public void destroy() {
}
@Override
public synchronized void clear() {
try {
openFile();
properties.clear();
write();
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
}
@Override
public synchronized Set keySet() {
Set result = null;
try {
openFile();
read();
result = properties.keySet();
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
@Override
public synchronized boolean containsKey(Object key) {
boolean result = false;
try {
openFile();
read();
result = properties.containsKey(key);
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
@Override
public synchronized boolean containsValue(Object value) {
boolean result = false;
try {
openFile();
read();
result = properties.containsValue(value);
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
@Override
public synchronized Set> entrySet() {
Set> result = null;
try {
openFile();
read();
result = properties.entrySet();
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
@Override
public synchronized Serializable get(Object key) {
Serializable result = null;
try {
openFile();
read();
result = properties.get(key);
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
@Override
public synchronized boolean isEmpty() {
boolean result = false;
try {
openFile();
read();
result = properties.isEmpty();
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
@Override
public synchronized Serializable put(String key, Serializable value) {
Serializable result = null;
try {
openFile();
read();
result = properties.put(key, value);
write();
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
@Override
public synchronized void putAll(
Map extends String, ? extends Serializable> map) {
try {
openFile();
read();
properties.putAll(map);
write();
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
}
@Override
public synchronized boolean putIfUnchanged(String key, Serializable newVal,
Serializable oldVal) {
boolean result = false;
try {
openFile();
read();
if (!(oldVal == null && properties.containsKey(key))
|| properties.get(key).equals(oldVal)) {
properties.put(key, newVal);
write();
result = true;
}
} catch (Exception e) {
e.printStackTrace();
result = true; // Don't let users loop if exception is thrown. They
// would get into a deadlock....
}
closeFile();
return result;
}
@Override
public synchronized Serializable remove(Object key) {
Serializable result = null;
try {
openFile();
read();
result = properties.remove(key);
write();
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
@Override
public synchronized int size() {
int result = -1;
try {
openFile();
read();
result = properties.size();
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
@Override
public synchronized Collection values() {
Collection result = null;
try {
openFile();
read();
result = properties.values();
} catch (Exception e) {
e.printStackTrace();
}
closeFile();
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy