/*
* Copyright (C) 2014 Jan Pokorsky
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cz.cas.lib.proarc.common.export.desa;
import cz.cas.lib.proarc.common.object.ValueMap;
import cz.cas.lib.proarc.common.object.model.MetaModel;
import cz.cas.lib.proarc.common.user.Group;
import cz.cas.lib.proarc.common.user.UserProfile;
import cz.cas.lib.proarc.common.user.UserUtil;
import cz.cas.lib.proarc.desa.DesaClient;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures.ACollects.ACollect;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures.AFunds.AFund;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures.CreCntrls.CreCntrl;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures.DocTypes.DocType;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures.Locs.Loc;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures.Pronoms.Pronom;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures.RdCntrls.RdCntrl;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures.RecCls.RecCl;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures.RecTypes.RecType;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXB;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.FileUtils;
/**
* Manages DESA service clients and their configurations.
*
* @author Jan Pokorsky
*/
public final class DesaServices {
/**
* The property name of list of all active DESA service IDs.
*/
public static final String PROPERTY_DESASERVICES = "desa.services";
/**
* The prefix of properties of the given service.
*/
public static final String PREFIX_DESA = "desa";
/**
* The type of remote DESA users and groups.
*/
public static final String REMOTE_USER_TYPE = "desa";
private static final Logger LOG = Logger.getLogger(DesaServices.class.getName());
private final Configuration config;
public DesaServices(Configuration config) {
this.config = config;
}
/**
* Gets value maps of nomenclatures.
* @param n nomenclature
* @param pluginId prefix for each added value map
* @return list of value maps
*/
public List<ValueMap> getValueMap(Nomenclatures n, String pluginId) {
ArrayList<ValueMap> maps = new ArrayList<ValueMap>();
if (n == null) {
return maps;
}
if (n.getACollects() != null) {
maps.add(new ValueMap<ACollect>(pluginId + ".a-collect", n.getACollects().getACollect()));
}
if (n.getAFunds() != null) {
maps.add(new ValueMap<AFund>(pluginId + ".a-fund", n.getAFunds().getAFund()));
}
if (n.getCreCntrls() != null) {
maps.add(new ValueMap<CreCntrl>(pluginId + ".cre-cntrl", n.getCreCntrls().getCreCntrl()));
}
if (n.getDocTypes() != null) {
maps.add(new ValueMap<DocType>(pluginId + ".doc-type", n.getDocTypes().getDocType()));
}
if (n.getLocs() != null) {
maps.add(new ValueMap<Loc>(pluginId + ".loc", n.getLocs().getLoc()));
}
if (n.getPronoms() != null) {
maps.add(new ValueMap<Pronom>(pluginId + ".pronom", n.getPronoms().getPronom()));
}
if (n.getRdCntrls() != null) {
maps.add(new ValueMap<RdCntrl>(pluginId + ".rd-cntrl", n.getRdCntrls().getRdCntrl()));
}
if (n.getRecCls() != null) {
maps.add(new ValueMap<RecCl>(pluginId + ".rec-cl", n.getRecCls().getRecCl()));
}
if (n.getRecTypes() != null) {
maps.add(new ValueMap<RecType>(pluginId + ".rec-type", n.getRecTypes().getRecType()));
}
return maps;
}
/**
* Gets nomenclatures.
* @param dc configuration to query
* @return nomenclatures or {@code null}
*/
public Nomenclatures getNomenclatures(DesaConfiguration dc, UserProfile user) {
List<String> acronyms = dc.getNomenclatureAcronyms();
if (acronyms.isEmpty()) {
return null;
} else {
return getNomenclaturesCache(dc, user);
}
}
private Nomenclatures getNomenclaturesCache(DesaConfiguration dc, UserProfile user) {
File tmpFolder = FileUtils.getTempDirectory();
File cache = null;
String operator = getOperatorName(user, dc);
if (operator == null || operator.isEmpty()) {
throw new IllegalStateException("Missing operator! id: "
+ dc.getServiceId() + ", user: " + (user == null ? null : user.getUserName()));
}
long expiration = dc.getNomenclatureExpiration();
if (expiration != 0) {
synchronized(DesaServices.this) {
// ensure the filename is platform safe and unique for each operator
String codeAsFilename = String.valueOf(operator.hashCode() & 0x00000000ffffffffL);
cache = new File(tmpFolder, String.format("%s.%s.nomenclatures.cache", dc.getServiceId(), codeAsFilename));
if (cache.exists() && (expiration < 0 || System.currentTimeMillis() - cache.lastModified() < expiration)) {
return JAXB.unmarshal(cache, Nomenclatures.class);
}
}
}
String producerCode = getProducerCode(user, dc);
if (producerCode == null || producerCode.isEmpty()) {
throw new IllegalStateException("Missing producer code! id: "
+ dc.getServiceId() + ", user: " + (user == null ? null : user.getUserName()));
}
DesaClient desaClient = getDesaClient(dc);
Nomenclatures nomenclatures = desaClient.getNomenclatures(
operator, producerCode, dc.getNomenclatureAcronyms());
if (cache != null) {
synchronized (DesaServices.this) {
JAXB.marshal(nomenclatures, cache);
}
}
return nomenclatures;
}
public DesaClient getDesaClient(DesaConfiguration dc) {
DesaClient desaClient = new DesaClient(dc.getSoapServiceUrl(),
dc.getRestServiceUrl(), dc.getUsername(), dc.getPassword());
return desaClient;
}
public String getOperatorName(UserProfile operator, DesaConfiguration dc) {
String operatorName = null;
if (operator != null) {
String remoteType = operator.getRemoteType();
if (REMOTE_USER_TYPE.equals(remoteType)) {
operatorName = operator.getRemoteName();
}
}
if (operatorName == null && dc != null) {
operatorName = dc.getOperator();
}
return operatorName;
}
public String getProducerCode(UserProfile operator, DesaConfiguration dc) {
String operatorName = null;
String producerCode = null;
if (operator != null) {
String remoteType = operator.getRemoteType();
Group group = null;
if (REMOTE_USER_TYPE.equals(remoteType)) {
operatorName = operator.getRemoteName();
if (operator.getDefaultGroup() != null) {
group = UserUtil.getDefaultManger().findGroup(operator.getDefaultGroup());
if (group != null && REMOTE_USER_TYPE.equals(group.getRemoteType())) {
producerCode = group.getRemoteName();
}
}
}
if (LOG.isLoggable(Level.FINE)) {
LOG.fine(String.format("Operator: userName: %s, name: %s, producerCode: %s, remoteType: %s, group: %s",
operator.getUserName(), operatorName, producerCode, remoteType, group));
}
}
if (producerCode == null && dc != null) {
producerCode = dc.getProducer();
}
return producerCode;
}
/**
* Finds a service configuration that accepts the passes model.
* The configuration must be listed in {@link #PROPERTY_DESASERVICES}.
*
* @param model digital object model
* @return the service configuration or {@code null}
*/
public DesaConfiguration findConfiguration(MetaModel model) {
if (model == null) {
return null;
}
for (String sid : config.getStringArray(PROPERTY_DESASERVICES)) {
DesaConfiguration dc = readConfiguration(config, sid);
if (dc.getExportModels().contains(model.getPid())) {
return dc;
}
}
return null;
}
/**
* Finds a service configuration. The configuration must be listed in
* {@link #PROPERTY_DESASERVICES}.
*
* @param serviceId service ID
* @return the service configuration or {@code null}
*/
public DesaConfiguration findConfiguration(String serviceId) {
for (String sid : config.getStringArray(PROPERTY_DESASERVICES)) {
if (sid.equals(serviceId)) {
return readConfiguration(config, sid);
}
}
return null;
}
/**
* Finds a configuration that declares any of the passed model IDs.
* @param modelIds mode IDs to query
* @return the service configuration or {@code null}
*/
public DesaConfiguration findConfigurationWithModel(String... modelIds) {
for (String sid : config.getStringArray(PROPERTY_DESASERVICES)) {
DesaConfiguration dc = readConfiguration(config, sid);
List<String> exportModels = dc.getExportModels();
for (String modelId : modelIds) {
if (exportModels.contains(modelId)) {
return dc;
}
}
}
return null;
}
/**
* Gets all configurations.
* @return the list of service configurations
*/
public List<DesaConfiguration> getConfigurations() {
return readConfigurations(config);
}
private List<DesaConfiguration> readConfigurations(Configuration config) {
ArrayList<DesaConfiguration> configs = new ArrayList<DesaConfiguration>();
for (String serviceId : config.getStringArray(PROPERTY_DESASERVICES)) {
configs.add(readConfiguration(config, serviceId));
}
return configs;
}
private DesaConfiguration readConfiguration(Configuration config, String serviceId) {
String servicePrefix = PREFIX_DESA + '.' + serviceId;
Configuration serviceConfig = config.subset(servicePrefix);
return new DesaConfiguration(serviceId, servicePrefix, serviceConfig);
}
/**
* The configuration for access to the DESA registry.
*/
public static final class DesaConfiguration {
public static final String PROPERTY_USER = "user";
public static final String PROPERTY_PASSWD = "password";
public static final String PROPERTY_PRODUCER = "producer";
public static final String PROPERTY_OPERATOR = "operator";
public static final String PROPERTY_RESTAPI = "restapi";
public static final String PROPERTY_WEBSERVICE = "webservice";
public static final String PROPERTY_EXPORTMODELS = "exportModels";
public static final String PROPERTY_NOMENCLATUREACRONYMS = "nomenclatureAcronyms";
public static final String PROPERTY_NOMENCLATUREEXPIRATION = "nomenclatureCacheExpiration";
private final String serviceId;
private final String prefix;
private final Configuration properties;
public DesaConfiguration(String serviceId, String prefix, Configuration properties) {
this.serviceId = serviceId;
this.prefix = prefix;
this.properties = properties;
}
public String getServiceId() {
return serviceId;
}
public String getSoapServiceUrl() {
return properties.getString(PROPERTY_WEBSERVICE);
}
public String getRestServiceUrl() {
return properties.getString(PROPERTY_RESTAPI);
}
public String getUsername() {
return properties.getString(PROPERTY_USER);
}
public String getPassword() {
return properties.getString(PROPERTY_PASSWD);
}
/**
* Gets a default operator name. Use for non DESA users.
* @return the operator name
*/
public String getOperator() {
return properties.getString(PROPERTY_OPERATOR);
}
/**
* Gets a default producer code. Use for non DESA users.
* @return the producer code
*/
public String getProducer() {
return properties.getString(PROPERTY_PRODUCER);
}
/**
* Digital object model IDs accepted by the registry.
* @return list of IDs
*/
public List<String> getExportModels() {
return Arrays.asList(properties.getStringArray(PROPERTY_EXPORTMODELS));
}
/**
* Gets nomenclature acronyms to query {@link Nomenclatures} for value maps from the registry.
* @return list of acronyms
*/
public List<String> getNomenclatureAcronyms() {
return Arrays.asList(properties.getStringArray(PROPERTY_NOMENCLATUREACRONYMS));
}
/**
* Gets time to hold caches. A negative value implies eternity. Zero implies no cache.
* @return time in milliseconds
*/
public long getNomenclatureExpiration() {
return properties.getLong(PROPERTY_NOMENCLATUREEXPIRATION, 0) * 60 * 1000;
}
@Override
public String toString() {
return "DesaConfiguration{" + "serviceId=" + serviceId + ", models=" + getExportModels()
+ ", acronyms=" + getNomenclatureAcronyms()
+ ", producerCode=" + getProducer()
+ ", operator=" + getOperator()
+ '}';
}
}
}