All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.almende.eve.state.file.ConcurrentSerializableFileState Maven / Gradle / Ivy

The newest version!
/*
 * Copyright: Almende B.V. (2014), Rotterdam, The Netherlands
 * License: The Apache Software License, Version 2.0
 */
package com.almende.eve.state.file;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.almende.eve.state.AbstractState;
import com.almende.eve.state.file.FileStateBuilder.FileStateProvider;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * The Class ConcurrentSerializableFileState.
 * 
 * @author jos
 * @author ludo
 * @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:
* AgentHost factory = AgentHost.getInstance(config);
* ConcurrentFileState state = new * ConcurrentFileState("agentId",".eveagents");
* state.put("key", "value");
* System.out.println(state.get("key")); // "value"
*/ public class ConcurrentSerializableFileState extends AbstractState { private static final Logger LOG = Logger.getLogger("ConcurrentFileState"); private String filename = null; private FileChannel channel = null; private FileLock lock = null; private InputStream fis = null; private OutputStream fos = null; private static Map locked = new ConcurrentHashMap(); private final Map properties = Collections .synchronizedMap(new HashMap()); /** * Instantiates a new concurrent serializable file state. * * @param agentId * the agent id * @param filename * the filename * @param service * the service * @param params * the params */ public ConcurrentSerializableFileState(final String agentId, final String filename, final FileStateProvider service, final ObjectNode params) { super(agentId, service, params); this.filename = filename; } /* * (non-Javadoc) * * TODO: Get rid of these, they are a performance hazard!:) * * @see java.lang.Object#finalize() */ @Override public void finalize() throws Throwable { closeFile(); super.finalize(); } /** * Open file. * * @throws IOException * Signals that an I/O exception has occurred. */ @SuppressWarnings("resource") protected void openFile() throws IOException { synchronized (locked) { while (locked.containsKey(filename) && locked.get(filename)) { try { locked.wait(); } catch (final InterruptedException e) { } } locked.put(filename, true); final File file = new File(filename); if (!file.exists()) { locked.put(filename, false); locked.notifyAll(); throw new IllegalStateException( "Warning: File doesn't exist (anymore):'" + filename + "'"); } channel = new RandomAccessFile(file, "rw").getChannel(); try { // TODO: add support for shared locks, allowing parallel reading // operations. lock = channel.lock(); } catch (final Exception e) { channel.close(); channel = null; lock = null; locked.put(filename, false); locked.notifyAll(); throw new IllegalStateException( "error, couldn't obtain file lock on:" + filename, e); } fis = new BufferedInputStream(Channels.newInputStream(channel)); fos = new BufferedOutputStream(Channels.newOutputStream(channel)); } } /** * Close file. */ protected void closeFile() { synchronized (locked) { if (lock != null && lock.isValid()) { try { lock.release(); } catch (final IOException e) { LOG.log(Level.WARNING, "", e); } } try { if (fos != null) { fos.close(); } if (fis != null) { fis.close(); } if (channel != null) { channel.close(); } } catch (final IOException e) { LOG.log(Level.WARNING, "", e); } channel = null; fis = null; fos = null; lock = null; locked.put(filename, false); locked.notifyAll(); } } /** * write properties to disk. * * @throws IOException * Signals that an I/O exception has occurred. */ private void write() throws IOException { if (channel != null) { channel.position(0); } final ObjectOutput out = new ObjectOutputStream(fos); out.writeObject(properties); out.flush(); if (channel != null) { channel.truncate(channel.position()); } } /** * read properties from disk. * * @throws IOException * Signals that an I/O exception has occurred. * @throws ClassNotFoundException * the class not found exception */ @SuppressWarnings("unchecked") private void read() throws IOException, ClassNotFoundException { try { if (channel != null) { channel.position(0); } properties.clear(); final ObjectInput in = new ObjectInputStream(fis); properties.putAll((Map) in.readObject()); } catch (final EOFException eof) { // empty file, new agent? } } /* * (non-Javadoc) * * @see com.almende.eve.state.State#clear() */ @Override public synchronized void clear() { try { openFile(); properties.clear(); write(); } catch (final IllegalStateException e) { LOG.log(Level.WARNING, "Statefile is missing: " + e.getMessage()); } catch (final Exception e) { LOG.log(Level.WARNING, "", e); } closeFile(); } /* * (non-Javadoc) * * @see com.almende.eve.state.State#keySet() */ @Override public synchronized Set keySet() { Set result = null; try { openFile(); read(); result = new HashSet(properties.keySet()); } catch (final IllegalStateException e) { LOG.log(Level.WARNING, "Statefile is missing: " + e.getMessage()); } catch (final Exception e) { LOG.log(Level.WARNING, "", e); } closeFile(); return result; } /* * (non-Javadoc) * * @see com.almende.eve.state.State#containsKey(java.lang.String) */ @Override public synchronized boolean containsKey(final String key) { boolean result = false; try { openFile(); read(); result = properties.containsKey(key); } catch (final IllegalStateException e) { LOG.log(Level.WARNING, "Statefile is missing: " + e.getMessage()); } catch (final Exception e) { LOG.log(Level.WARNING, "", e); } closeFile(); return result; } /* * (non-Javadoc) * * @see com.almende.eve.state.AbstractState#get(java.lang.String) */ @Override public synchronized Serializable get(final String key) { Serializable result = null; try { openFile(); read(); result = properties.get(key); } catch (final IllegalStateException e) { LOG.log(Level.WARNING, "Statefile is missing: " + e.getMessage()); } catch (final Exception e) { LOG.log(Level.WARNING, "", e); } closeFile(); return result; } /* * (non-Javadoc) * * @see com.almende.eve.state.AbstractState#locPut(java.lang.String, * java.io.Serializable) */ @Override public synchronized Serializable locPut(final String key, final Serializable value) { Serializable result = null; try { openFile(); read(); result = properties.put(key, value); write(); } catch (final IllegalStateException e) { LOG.log(Level.WARNING, "Statefile is missing: " + e.getMessage()); } catch (final Exception e) { LOG.log(Level.WARNING, "", e); } closeFile(); return result; } /* * (non-Javadoc) * * @see * com.almende.eve.state.AbstractState#locPutIfUnchanged(java.lang.String, * java.io.Serializable, java.io.Serializable) */ @Override public synchronized boolean locPutIfUnchanged(final String key, final Serializable newVal, final Serializable oldVal) { boolean result = false; try { openFile(); read(); if (!(oldVal == null && properties.containsKey(key) && properties .get(key) != null) || (properties.get(key) != null && properties.get(key) .equals(oldVal))) { properties.put(key, newVal); write(); result = true; } } catch (final IllegalStateException e) { LOG.log(Level.WARNING, "Statefile is missing: " + e.getMessage()); } catch (final Exception e) { LOG.log(Level.WARNING, "", e); // Don't let users loop if exception is thrown. They // would get into a deadlock.... result = true; } closeFile(); return result; } /* * (non-Javadoc) * * @see com.almende.eve.state.State#remove(java.lang.String) */ @Override public synchronized Object remove(final String key) { Object result = null; try { openFile(); read(); result = properties.remove(key); write(); } catch (final IllegalStateException e) { LOG.log(Level.WARNING, "Statefile is missing: " + e.getMessage()); } catch (final Exception e) { LOG.log(Level.WARNING, "", e); } closeFile(); return result; } /* * (non-Javadoc) * * @see com.almende.eve.state.State#size() */ @Override public synchronized int size() { int result = -1; try { openFile(); read(); result = properties.size(); } catch (final IllegalStateException e) { LOG.log(Level.WARNING, "Statefile is missing: " + e.getMessage()); } catch (final Exception e) { LOG.log(Level.WARNING, "", e); } closeFile(); return result; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy