/*
* Copyright (C) 2007 ETH Zurich
*
* This file is part of Fosstrak (www.fosstrak.org).
*
* Fosstrak is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* Fosstrak is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Fosstrak; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package org.fosstrak.ale.server.readers.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.fosstrak.ale.exception.DuplicateNameException;
import org.fosstrak.ale.exception.ImmutableReaderException;
import org.fosstrak.ale.exception.ImplementationException;
import org.fosstrak.ale.exception.InUseException;
import org.fosstrak.ale.exception.NoSuchNameException;
import org.fosstrak.ale.exception.NonCompositeReaderException;
import org.fosstrak.ale.exception.ReaderLoopException;
import org.fosstrak.ale.exception.SecurityException;
import org.fosstrak.ale.exception.ValidationException;
import org.fosstrak.ale.server.ALE;
import org.fosstrak.ale.server.ALESettings;
import org.fosstrak.ale.server.ac.ALEACImpl;
import org.fosstrak.ale.server.persistence.RemoveConfig;
import org.fosstrak.ale.server.persistence.WriteConfig;
import org.fosstrak.ale.server.readers.BaseReader;
import org.fosstrak.ale.server.readers.CompositeReader;
import org.fosstrak.ale.server.readers.LogicalReader;
import org.fosstrak.ale.server.readers.LogicalReaderManager;
import org.fosstrak.ale.server.readers.impl.type.ReaderProvider;
import org.fosstrak.ale.xsd.ale.epcglobal.ECSpec;
import org.fosstrak.ale.xsd.ale.epcglobal.LRProperty;
import org.fosstrak.ale.xsd.ale.epcglobal.LRSpec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* standard implementation of the LogicalReaderManager.
*
* @author swieland
*
*/
@Service("logicalReaderManager")
public class LogicalReaderManagerImpl implements LogicalReaderManager {
/** logger. */
private static final Logger LOG = Logger.getLogger(LogicalReaderManager.class);
/** a map of all LogicalReaders. the readers are mapped against their name. */
private java.util.Map<String, LogicalReader> logicalReaders = new ConcurrentHashMap<String, LogicalReader>();
// autowired
private RemoveConfig persistenceRemoveAPI;
// autowired
private WriteConfig persistenceWriteAPI;
// autowired
private ReaderProvider readerProvider;
// autowired
private ALESettings aleSettings;
@Autowired
private ALE ale;
@Autowired
private ALEACImpl aleac;
private String authScope = "ALELR";
public ALEACImpl getAleac() {
return aleac;
}
public void setAleac(ALEACImpl aleac) {
this.aleac = aleac;
}
@Override
public String getVendorVersion() throws ImplementationException {
return aleSettings.getVendorVersion();
}
@Override
public String getStandardVersion() throws ImplementationException {
return aleSettings.getLrStandardVersion();
}
@Override
public String getPropertyValue(String name, String propertyName) throws NoSuchNameException, SecurityException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
LogicalReader logRd = logicalReaders.get(name);
throwNoSuchNameExceptionIfReaderNull(logRd, name);
List<LRProperty> propList = logRd.getProperties();
for (LRProperty prop : propList) {
if (prop.getName().equalsIgnoreCase(propertyName)) {
return prop.getValue();
}
}
return null;
}
@Override
public void setProperties(String name, List<LRProperty> properties) throws NoSuchNameException, ValidationException, InUseException, ImmutableReaderException, SecurityException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
throwValidationExceptionOnNullInput(properties);
// throw validation exception on invalid isComposite property
for(LRProperty prop : properties) {
if(prop.getName().equals("isComposite")) {
if(!prop.getValue().equalsIgnoreCase("true") && !prop.getValue().equalsIgnoreCase("false")) {
throw new ValidationException("isComposite should be either true or false");
}
}
}
LogicalReader logRd = logicalReaders.get(name);
throwNoSuchNameExceptionIfReaderNull(logRd, name);
LRSpec spec = logRd.getLRSpec();
if (spec.getProperties() == null) {
spec.setProperties(new LRSpec.Properties());
}
// we need to replace the properties, not just add to the old ones.
spec.getProperties().getProperty().clear();
spec.getProperties().getProperty().addAll(properties);
LOG.debug("set the properties");
// previously it was update method (it causes spagetti code, so I removed)
throwNoSuchNameExceptionIfReaderNull(logRd, name);
// check if each reader in a list of spec exists or not
if(spec.getReaders() != null) {
for(String readerName : spec.getReaders().getReader()) {
if(!logicalReaders.containsKey(readerName)) {
throw new ValidationException("no such reader");
}
}
}
int numObservers = logRd.countObservers();
if(numObservers > 0) {
throw new InUseException("logical reader is in use");
}
logRd.update(spec);
persistenceRemoveAPI.removeLRSpec(name);
persistenceWriteAPI.writeLRSpec(name, spec);
}
@Override
public void removeReaders(String name, java.util.List<String> readers) throws NoSuchNameException, InUseException, ImmutableReaderException, NonCompositeReaderException, SecurityException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
LogicalReader lgRd = logicalReaders.get(name);
throwNoSuchNameExceptionIfReaderNull(lgRd, name);
throwNonCompositeReaderExceptionIfReaderNotComposite(lgRd, name);
// get the readers that are still in the spec
LRSpec spec = lgRd.getLRSpec();
List<String> res = new ArrayList<String>();
if ((spec.getReaders() != null) && (spec.getReaders().getReader().size() > 0)) {
for (String reader : spec.getReaders().getReader()) {
if (!readers.contains(reader)) {
res.add(reader);
}
}
}
// add the resulting readers
spec.setReaders(new LRSpec.Readers());
spec.getReaders().getReader().addAll(res);
try {
update(name, spec);
} catch (ValidationException e) {
throw new ImplementationException(e.getMessage());
} catch (ReaderLoopException e) {
throw new ImplementationException(e.getMessage());
}
}
@Override
public void setReaders(String name, java.util.List<String> readers) throws NoSuchNameException, ValidationException, InUseException, ImmutableReaderException, NonCompositeReaderException, ReaderLoopException, SecurityException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
LogicalReader logRd = logicalReaders.get(name);
throwNoSuchNameExceptionIfReaderNull(logRd, name);
throwNonCompositeReaderExceptionIfReaderNotComposite(logRd, name);
throwValidationExceptionIfNotAllReadersAvailable(readers);
LRSpec spec = logRd.getLRSpec();
spec.setReaders(new LRSpec.Readers());
spec.getReaders().getReader().addAll(readers);
update(name, spec);
}
@Override
public void addReaders(String name, java.util.List<String> readers) throws NoSuchNameException, ValidationException, InUseException, ImmutableReaderException, ReaderLoopException, SecurityException, ImplementationException, NonCompositeReaderException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
LogicalReader logRd = logicalReaders.get(name);
throwNoSuchNameExceptionIfReaderNull(logRd, name);
throwNonCompositeReaderExceptionIfReaderNotComposite(logRd, name);
throwValidationExceptionIfNotAllReadersAvailable(readers);
LRSpec spec = logRd.getLRSpec();
if (spec.getReaders() == null) {
spec.setReaders(new LRSpec.Readers());
}
for (String reader : readers) {
if (!spec.getReaders().getReader().contains(reader)) {
spec.getReaders().getReader().add(reader);
}
}
update(name, spec);
}
@Override
public LRSpec getLRSpec(String name) throws NoSuchNameException, SecurityException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
LogicalReader logRd = logicalReaders.get(name);
throwNoSuchNameExceptionIfReaderNull(logRd, name);
return logRd.getLRSpec();
}
@Override
public java.util.List<String> getLogicalReaderNames() throws SecurityException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
List<String> rdNames = new ArrayList<String>();
Iterable<String> it = logicalReaders.keySet();
for (String reader : it) {
rdNames.add(reader);
}
return rdNames;
}
@Override
public void undefine(String name) throws NoSuchNameException, InUseException, SecurityException, ImmutableReaderException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
// the logicalReader must delete himself from its observables
LOG.debug("undefining reader " + name);
LogicalReader reader = getLogicalReader(name);
throwNoSuchNameExceptionIfReaderNull(reader, name);
// according to the EPC standard a reader cannot be undefined when there is
// an active CC or EC pointing to the reader
// this raises an InUseException
if (reader.countObservers() > 0) {
throw new InUseException(name + "is still in use.");
}
for(String ecspecName : ale.getECSpecNames()) {
ECSpec ecspec = ale.getECSpec(ecspecName);
if(ecspec.getLogicalReaders().getLogicalReader().contains(name)) {
throw new InUseException(name + "is still in use.");
}
}
if (reader instanceof CompositeReader) {
CompositeReader composite = (CompositeReader) reader;
composite.unregisterAsObserver();
} else if (reader instanceof BaseReader) {
BaseReader basereader = (BaseReader) reader;
basereader.disconnectReader();
basereader.cleanup();
} else {
throw new ImplementationException("try to undefine unknown reader type - ALE knows BaseReader and CompositeReader - atomic readers must subclass BaseReader, composite readers (collections of readers) must subclass CompositeReader - this is a serious problem!!! reader-name: " + name);
}
persistenceRemoveAPI.removeLRSpec(name);
logicalReaders.remove(name);
}
@Override
public void update(String name, LRSpec spec) throws NoSuchNameException, ValidationException, InUseException, ImmutableReaderException, ReaderLoopException, SecurityException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
LogicalReader logRd = logicalReaders.get(name);
throwNoSuchNameExceptionIfReaderNull(logRd, name);
// check if each reader in a list of spec exists or not
if(spec.getReaders() != null) {
for(String readerName : spec.getReaders().getReader()) {
if(!logicalReaders.containsKey(readerName)) {
throw new ValidationException("no such reader");
}
if(readerName.equals(name)) {
throw new ReaderLoopException("reader loop detected");
}
}
}
int numObservers = logRd.countObservers();
if(numObservers > 0) {
throw new InUseException("logical reader is in use");
}
logRd.update(spec);
persistenceRemoveAPI.removeLRSpec(name);
persistenceWriteAPI.writeLRSpec(name, spec);
}
@Override
public void define(String name, org.fosstrak.ale.server.readers.gen.LRSpec spec) throws DuplicateNameException, ValidationException, SecurityException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
throwValidationExceptionOnNullInput(name, "parameter name is null");
throwValidationExceptionOnNullInput(spec, "parameter spec is null");
LRSpec thespec = new LRSpec();
// add the readers
thespec.setReaders(new LRSpec.Readers());
if (spec.getReaders() != null) {
thespec.getReaders().getReader().addAll(spec.getReaders());
}
// set if composite reader or basereader
thespec.setIsComposite(spec.isIsComposite());
// set the properties
thespec.setProperties(new LRSpec.Properties());
for (org.fosstrak.ale.server.readers.gen.LRProperty prop : spec.getLRProperty()) {
LRProperty property = new LRProperty();
property.setName(prop.getName());
property.setValue(prop.getValue());
thespec.getProperties().getProperty().add(property);
}
// at the ReaderType property
LRProperty property = new LRProperty();
property.setName(LogicalReader.PROPERTY_READER_TYPE);
property.setValue(spec.getReaderType());
thespec.getProperties().getProperty().add(property);
define(name, thespec);
}
@Override
public void define(String name, LRSpec spec) throws DuplicateNameException, ValidationException, SecurityException, ImplementationException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
LOG.debug("define");
throwValidationExceptionOnNullInput(name, "parameter name is null");
throwValidationExceptionOnNullInput(spec, "parameter spec is null");
if(logicalReaders.containsKey(name)) {
throw new DuplicateNameException("LRSpec "+name+" already exists");
}
LogicalReader logRead = getReaderProvider().createReader(name, spec);
// establish connection when basereader
if (logRead instanceof BaseReader) {
//((BaseReader)logRead).connectReader(); Call me if you got trouble, littleanti91@gmail.com
}
persistenceWriteAPI.writeLRSpec(name, spec);
LOG.debug("saving reader: " + name + " " + logRead.getClass().getCanonicalName());
logicalReaders.put(name, logRead);
LOG.debug("successfully executed define");
}
@Override
public LogicalReader getLogicalReader(String readerName) {
LogicalReader reader = null;
if (logicalReaders.containsKey(readerName)) {
reader = logicalReaders.get(readerName);
if (!reader.isStarted()) {
reader.start();
}
}
return reader;
}
@Override
public Collection<LogicalReader> getLogicalReaders() {
return logicalReaders.values();
}
@Override
public void setLogicalReader(LogicalReader reader) throws ImplementationException, SecurityException {
aleac.checkAccess(authScope, Thread.currentThread().getStackTrace()[1].getMethodName());
if (logicalReaders.containsKey(reader.getName())) {
throw new ImplementationException("reader duplicated");
}
logicalReaders.put(reader.getName(), reader);
}
@Override
public boolean contains(String logicalReaderName) {
return logicalReaders.containsKey(logicalReaderName);
}
/**
* assert that the given reader is not null. if so, throws Exception.
* @param logRd the logical reader to test.
* @param name the name of the reader.
* @throws NoSuchNameException if the given reader is null.
*/
protected void throwNoSuchNameExceptionIfReaderNull(LogicalReader logRd, String name) throws NoSuchNameException {
if (null == logRd) {
String errMsg = String.format("There is no such reader %s", name);
LOG.debug(errMsg);
throw new NoSuchNameException(errMsg);
}
}
/**
* assert that the given reader is a composite reader. if not, throws exception.
* @param logRd the logical reader that is to be tested.
* @param name the name of the reader.
* @throws NonCompositeReaderException if the given reader is not composite.
*/
protected void throwNonCompositeReaderExceptionIfReaderNotComposite(LogicalReader logRd, String name) throws NonCompositeReaderException {
if (! (logRd instanceof CompositeReader)) {
String errMsg = "reader " + name + " is not composite";
LOG.debug(errMsg);
throw new NonCompositeReaderException(errMsg);
}
}
/**
* assert that the given input parameter is not null. if so, throws exception.
* @param inputParameter the input parameter to test on null.
* @throws ValidationException if the given input parameter is null.
*/
protected void throwValidationExceptionOnNullInput(Object inputParameter, String... parameters) throws ValidationException {
if (null == inputParameter) {
String errMsg = "given input parameter is null. " + StringUtils.join(parameters);
LOG.debug(errMsg);
throw new ValidationException(errMsg);
}
}
/**
* assert that the given list of readers are all present/defined.
* @param readers the readers to test.
* @throws ValidationException if at least one of the requested readers is not existing.
*/
protected void throwValidationExceptionIfNotAllReadersAvailable(List<String> readers) throws ValidationException {
for (String readerName : readers) {
if (!contains(readerName)) {
throw new ValidationException("the requested reader is not defined: " + readerName);
}
}
}
/**
* a handle to the currently used reader provider.
* @return the currently used reader provider.
*/
public ReaderProvider getReaderProvider() {
return readerProvider;
}
/**
* allow to inject a new reader provider.
* @param readerProvider the new reader provider to be used.
*/
@Autowired
public void setReaderProvider(ReaderProvider readerProvider) {
this.readerProvider = readerProvider;
}
/**
* allow to inject a new ALESettings.
* @param aleSettings the new ALESettings to be used.
*/
@Autowired
public void setAleSettings(ALESettings aleSettings) {
this.aleSettings = aleSettings;
}
/**
* allow to inject the persistence delete API.
* @param persistenceRemoveAPI the persistence delete API.
*/
@Autowired
public void setPersistenceRemoveAPI(RemoveConfig persistenceRemoveAPI) {
this.persistenceRemoveAPI = persistenceRemoveAPI;
}
/**
* allow to inject the persistence write API.
* @param persistenceWriteAPI the persistence write API.
*/
@Autowired
public void setPersistenceWriteAPI(WriteConfig persistenceWriteAPI) {
this.persistenceWriteAPI = persistenceWriteAPI;
}
}