/*
* Copyright to the original author or authors.
*
* 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.rioproject.cybernode.service;
import com.sun.jini.config.Config;
import net.jini.config.Configuration;
import org.rioproject.deploy.ServiceStatement;
import org.rioproject.deploy.ServiceStatementManager;
import org.rioproject.opstring.ServiceElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.*;
/**
* The PersistentServiceStatementManager provides an implementation of the
* ServiceStatementManager, reading and writing ServiceStatement instances to
* the file system
*
* @author Dennis Reedy
*/
public class PersistentServiceStatementManager implements ServiceStatementManager {
/** The directory to write and read ServiceRecord instances */
File recordRoot;
/** Filename extension */
final static String STATEMENT_EXT = ".stmt";
static final long SECOND = 1000;
static final long MINUTE = SECOND * 60;
static final long HOUR = MINUTE * 60;
static final long DAY = HOUR * 24;
static final long ETERNITY = 0;
/**
* If a ServiceStatement is older then this value, remove it from the
* filesystem
*/
long clean = DAY * 30;
/** Timer to shedule FileSweeper tasks */
Timer taskTimer;
/** A semaphore for reading/writing */
final Object rwSemaphore = new Object();
/** Logger */
static Logger logger = LoggerFactory.getLogger("org.rioproject.cybernode");
/**
* Create a PersistentServiceStatementManager
*
* @param config The configuration to use
*/
public PersistentServiceStatementManager(Configuration config) {
try {
recordRoot = Environment.setupRecordRoot(config);
logger.debug("Storing service statements in [{}]", recordRoot.getCanonicalPath());
long age = clean;
try {
age = Config.getLongEntry(config,
CybernodeImpl.getConfigComponent(),
"recordAge",
30,
0,
Long.MAX_VALUE);
age = age * DAY;
} catch(Throwable t) {
logger.warn("PersistentServiceStatementManager : exception getting recordAge", t);
}
if(age != ETERNITY) {
logger.debug( "ServiceStatement maximum age [{}] days", (age / DAY));
taskTimer = new Timer(true);
long now = System.currentTimeMillis();
taskTimer.scheduleAtFixedRate(new FileSweeper(age), new Date(now + SECOND), HOUR);
} else {
logger.debug("ServiceStatements live forever");
}
} catch(IOException e) {
logger.warn("Accessing storage for ServiceRecords", e);
}
}
/**
* @see org.rioproject.deploy.ServiceStatementManager#terminate
*/
public void terminate() {
if(taskTimer != null)
taskTimer.cancel();
}
/**
* @see org.rioproject.deploy.ServiceStatementManager#get
*/
public ServiceStatement[] get() {
List<ServiceStatement> list = new ArrayList<ServiceStatement>();
File[] statements = recordRoot.listFiles();
if(statements!=null) {
for (File statement : statements) {
try {
ServiceStatement stmnt = read(statement);
if (stmnt != null)
list.add(stmnt);
} catch (Exception e) {
logger.error("Getting ServiceStatement instances", e);
}
}
}
return (list.toArray(new ServiceStatement[list.size()]));
}
/**
* @see org.rioproject.deploy.ServiceStatementManager#get
*/
public ServiceStatement get(ServiceElement sElem) {
ServiceStatement statement = null;
try {
statement = read(makeName(sElem) + STATEMENT_EXT);
} catch(IOException e) {
logger.error("Getting ServiceStatement", e);
} catch(ClassNotFoundException e) {
logger.error("Getting ServiceStatement", e);
}
return (statement);
}
/**
* @see org.rioproject.deploy.ServiceStatementManager#record
*/
public void record(ServiceStatement statement) {
try {
write(statement);
} catch(IOException e) {
logger.error("Writing ServiceStatement", e);
}
}
/*
* Write a ServiceStatement to the file system
*
* @param statement The ServiceStatement to write
*/
void write(ServiceStatement statement) throws IOException {
synchronized(rwSemaphore) {
File record = new File(recordRoot, makeName(statement.getServiceElement())+STATEMENT_EXT);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(record));
oos.writeObject(statement);
oos.flush();
}
}
/*
* Read a ServiceStatement from the file system
*
* @param fileName The ServiceStatement file name
* @return ServiceStatement
*/
ServiceStatement read(String fileName) throws ClassNotFoundException, IOException {
File file = new File(recordRoot, fileName);
if(file.exists())
return (read(file));
return (null);
}
/*
* Read a ServiceStatement from the file system
*
* @param input The input File
* @return ServiceStatement
*/
ServiceStatement read(File input) throws ClassNotFoundException,
IOException {
synchronized(rwSemaphore) {
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream(input));
return ((ServiceStatement)ois.readObject());
}
}
/*
* Make a name for the ServiceElement
*/
String makeName(ServiceElement sElem) {
return(sElem.getOperationalStringName()+"."+sElem.getName());
}
/**
* The FileSweeper class is scheduled every hour to sweep the directory an
* determine if any ServiceStatement files are older then the allotted age
* (default is a month). If the files are older, and all the ServiceRecord
* instances within the ServiceStatement are INACTIVE, then remove the
* ServiceStatement
*/
class FileSweeper extends TimerTask {
long oldAge;
FileSweeper(long oldAge) {
this.oldAge = oldAge;
}
/**
* The action to be performed by this timer task.
*/
public void run() {
long now = System.currentTimeMillis();
File[] files = recordRoot.listFiles();
if(files!=null) {
for (File file : files) {
if (file.getName().endsWith(STATEMENT_EXT)) {
long lastModified = file.lastModified();
long age = now - lastModified;
if (age > oldAge) {
file.delete();
if (logger.isDebugEnabled())
logger.debug("ServiceStatement [{}] has aged past [{}] days, file has been removed",
file.getName(), (oldAge/ DAY));
}
}
}
}
}
}
}