/*
* Copyright (c) Members of the EGEE Collaboration. 2006-2010.
* See http://www.eu-egee.org/partners/ for details on the copyright holders.
*
* 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.glite.authz.pep.obligation.dfpmap;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import org.glite.authz.common.config.ConfigurationException;
import org.glite.authz.common.util.Files;
import org.glite.authz.common.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A {@link DFPM} implementation that periodically re-reads a mapping file and, if changes have occurred, updates the
* mapping. Such an update does not effect any
*/
public class UpdatingDFPM implements DFPM {
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(UpdatingDFPM.class);
/** Delegate that is refreshed every period. */
private DFPM delegate;
/** Timer used to run the background mapping file refresh. */
private Timer taskTimer;
/** Factory used to create empty {@link DFPM}s. */
private final DFPMFactory dfpmFactory;
/** Path to the mapping file that will be periodically reloaded. */
private final String mappingFilePath;
/**
* Constructor.
*
* @param factory factory that produces an empty {@link DFPM} into which data will be loaded
* @param mappingFile mapping file that will be periodically read and loaded in to the created {@link DFPM}
* @param refreshPeriod {@link DFPM} refresh period in milliseconds
*/
public UpdatingDFPM(DFPMFactory factory, String mappingFile, long refreshPeriod) {
if(factory == null){
throw new IllegalArgumentException("mapping factory may not be null");
}
dfpmFactory = factory;
mappingFilePath = Strings.safeTrimOrNullString(mappingFile);
if(mappingFilePath == null){
throw new IllegalArgumentException("mapping file may not be null");
}
try {
Files.getReadableFile(mappingFilePath);
} catch (IOException e) {
log.error("Unable to read map file " + mappingFilePath, e);
throw new IllegalArgumentException("Unable to read map file " + mappingFilePath, e);
}
if(refreshPeriod < 1){
throw new IllegalArgumentException("Refresh period must be 1 or greater");
}
UpdateDFPMTask updateTask = new UpdateDFPMTask();
updateTask.run();
taskTimer = new Timer(true);
taskTimer.scheduleAtFixedRate(updateTask, refreshPeriod, refreshPeriod);
}
/** {@inheritDoc} */
public boolean isDNMapEntry(String key) {
return delegate.isDNMapEntry(key);
}
/** {@inheritDoc} */
public boolean isFQANMapEntry(String key) {
return delegate.isFQANMapEntry(key);
}
/** {@inheritDoc} */
public void clear() {
delegate.clear();
}
/** {@inheritDoc} */
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
/** {@inheritDoc} */
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
/** {@inheritDoc} */
public Set<java.util.Map.Entry<String, List<String>>> entrySet() {
return delegate.entrySet();
}
/** {@inheritDoc} */
public List<String> get(Object key) {
return delegate.get(key);
}
/** {@inheritDoc} */
public boolean isEmpty() {
return delegate.isEmpty();
}
/** {@inheritDoc} */
public Set<String> keySet() {
return delegate.keySet();
}
/** {@inheritDoc} */
public List<String> put(String key, List<String> value) {
return delegate.put(key, value);
}
/** {@inheritDoc} */
public void putAll(Map<? extends String, ? extends List<String>> map) {
delegate.putAll(map);
}
/** {@inheritDoc} */
public List<String> remove(Object key) {
return delegate.remove(key);
}
/** {@inheritDoc} */
public int size() {
return delegate.size();
}
/** {@inheritDoc} */
public Collection<List<String>> values() {
return delegate.values();
}
/** Background task for updating a {@link DFPM}. */
private class UpdateDFPMTask extends TimerTask {
/** Local time the mapping file was last modified. */
private long mappingFileLastModified;
/** {@inheritDoc} */
public void run() {
try {
log.trace("Refreshing mapping file: {}", mappingFilePath);
File mappingFile = Files.getReadableFile(mappingFilePath);
if(mappingFile.lastModified() <= mappingFileLastModified){
log.trace("Mapping file has not changed since last refresh, nothing need to be done.");
return;
}
DFPM dfpm = dfpmFactory.newInstance();
DFPMFileParser mappingFileParser = new DFPMFileParser();
mappingFileParser.parse(dfpm, new FileReader(mappingFile));
mappingFileLastModified = mappingFile.lastModified();
delegate = dfpm;
} catch (IOException e) {
log.error("Unable to read mapping file " + mappingFilePath
+ " due to the following error. DN/FQAN mapping will not be updated.", e);
} catch (ConfigurationException e) {
log.error(
"Unable to parse mapping file " + mappingFilePath + ". DN/FQAN mapping will not be updated.",
e);
}
}
}
/** Factory used to create a new instance of a {@link DFPM}. */
public static interface DFPMFactory {
/**
* Creates a new, empty {@link DFPM} instance.
*
* @return new, empty {@link DFPM} instance
*/
public DFPM newInstance();
}
}