/*
* Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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.
*/
package com.agiletec.plugins.jacms.aps.system.services.resource;
import com.agiletec.aps.system.SystemConstants;
import com.agiletec.aps.system.common.AbstractService;
import com.agiletec.aps.system.common.FieldSearchFilter;
import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.aps.system.services.category.CategoryUtilizer;
import com.agiletec.aps.system.services.category.ICategoryManager;
import com.agiletec.aps.system.services.category.ReloadingCategoryReferencesThread;
import com.agiletec.aps.system.services.group.GroupUtilizer;
import com.agiletec.aps.system.services.keygenerator.IKeyGeneratorManager;
import com.agiletec.aps.util.DateConverter;
import com.agiletec.plugins.jacms.aps.system.services.resource.event.ResourceChangedEvent;
import com.agiletec.plugins.jacms.aps.system.services.resource.model.AbstractMonoInstanceResource;
import com.agiletec.plugins.jacms.aps.system.services.resource.model.AbstractMultiInstanceResource;
import com.agiletec.plugins.jacms.aps.system.services.resource.model.ResourceDataBean;
import com.agiletec.plugins.jacms.aps.system.services.resource.model.ResourceInstance;
import com.agiletec.plugins.jacms.aps.system.services.resource.model.ResourceInterface;
import com.agiletec.plugins.jacms.aps.system.services.resource.model.ResourceRecordVO;
import com.agiletec.plugins.jacms.aps.system.services.resource.parse.ResourceHandler;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
/**
* Servizio gestore tipi di risorse (immagini, audio, video, etc..).
* @author W.Ambu - E.Santoboni
*/
public class ResourceManager extends AbstractService
implements IResourceManager, GroupUtilizer, CategoryUtilizer {
private static final Logger _logger = LoggerFactory.getLogger(ResourceManager.class);
@Override
public void init() throws Exception {
_logger.debug("{} ready. Initialized {} resource types", this.getClass().getName(), this._resourceTypes.size());
}
/**
* Crea una nuova istanza di un tipo di risorsa del tipo richiesto. Il nuovo
* tipo di risorsa è istanziato mediante clonazione del prototipo corrispondente.
* @param typeCode Il codice del tipo di risorsa richiesto, come definito in configurazione.
* @return Il tipo di risorsa istanziato (vuoto).
*/
@Override
public ResourceInterface createResourceType(String typeCode) {
ResourceInterface resource = (ResourceInterface) _resourceTypes.get(typeCode);
return resource.getResourcePrototype();
}
/**
* Restituisce la lista delle chiavi dei tipi risorsa presenti nel sistema.
* @return La lista delle chiavi dei tipi risorsa esistenti.
*/
@Override
public List<String> getResourceTypeCodes() {
return new ArrayList<String>(this._resourceTypes.keySet());
}
/**
* Salva una risorsa nel db con incluse nel filesystem, indipendentemente dal tipo.
* @param bean L'oggetto detentore dei dati della risorsa da inserire.
* @throws ApsSystemException in caso di errore.
*/
@Override
public ResourceInterface addResource(ResourceDataBean bean) throws ApsSystemException {
ResourceInterface newResource = this.createResource(bean);
try {
this.generateAndSetResourceId(newResource, bean.getResourceId());
newResource.saveResourceInstances(bean);
this.getResourceDAO().addResource(newResource);
} catch (Throwable t) {
newResource.deleteResourceInstances();
_logger.error("Error adding resource", t);
throw new ApsSystemException("Error adding resource", t);
}
return newResource;
}
/**
* Salva una risorsa nel db, indipendentemente dal tipo.
* @param resource La risorsa da salvare.
* @throws ApsSystemException in caso di errore.
*/
@Override
public void addResource(ResourceInterface resource) throws ApsSystemException {
try {
this.generateAndSetResourceId(resource, resource.getId());
this.getResourceDAO().addResource(resource);
} catch (Throwable t) {
_logger.error("Error adding resource", t);
throw new ApsSystemException("Error adding resource", t);
}
}
protected void generateAndSetResourceId(ResourceInterface resource, String id) throws ApsSystemException {
if (null == id || id.trim().length() == 0) {
IKeyGeneratorManager keyGenerator =
(IKeyGeneratorManager) this.getBeanFactory().getBean(SystemConstants.KEY_GENERATOR_MANAGER);
int newId = keyGenerator.getUniqueKeyCurrentValue();
resource.setId(String.valueOf(newId));
}
}
@Override
public void updateResource(ResourceDataBean bean) throws ApsSystemException {
ResourceInterface oldResource = this.loadResource(bean.getResourceId());
try {
if (null == bean.getInputStream()) {
oldResource.setDescription(bean.getDescr());
oldResource.setCategories(bean.getCategories());
this.getResourceDAO().updateResource(oldResource);
this.notifyResourceChanging(oldResource);
} else {
ResourceInterface updatedResource = this.createResource(bean);//this.saveResource(bean);
updatedResource.saveResourceInstances(bean);
this.getResourceDAO().updateResource(updatedResource);
if (!updatedResource.getMasterFileName().equals(oldResource.getMasterFileName())) {
oldResource.deleteResourceInstances();
}
this.notifyResourceChanging(updatedResource);
}
} catch (Throwable t) {
_logger.error("Error updating resource", t);
throw new ApsSystemException("Error updating resource", t);
}
}
/**
* Aggiorna una risorsa nel db.
* @param resource Il contenuto da aggiungere o modificare.
* @throws ApsSystemException in caso di errore nell'accesso al db.
*/
@Override
public void updateResource(ResourceInterface resource) throws ApsSystemException {
try {
this.getResourceDAO().updateResource(resource);
this.notifyResourceChanging(resource);
} catch (Throwable t) {
_logger.error("Error updating resource", t);
throw new ApsSystemException("Error updating resource", t);
}
}
protected ResourceInterface createResource(ResourceDataBean bean) throws ApsSystemException {
ResourceInterface resource = this.createResourceType(bean.getResourceType());
resource.setDescription(bean.getDescr());
resource.setMainGroup(bean.getMainGroup());
resource.setCategories(bean.getCategories());
resource.setMasterFileName(bean.getFileName());
resource.setId(bean.getResourceId());
return resource;
}
protected void notifyResourceChanging(ResourceInterface resource) throws ApsSystemException {
ResourceChangedEvent event = new ResourceChangedEvent();
event.setResource(resource);
this.notifyEvent(event);
}
/**
* Carica una lista di identificativi di risorse
* in base al tipo, ad una parola chiave e dalla categoria della risorsa.
* @param type Tipo di risorsa da cercare.
* @param text Testo immesso per il raffronto con la descrizione della risorsa. null o
* stringa vuota nel caso non si voglia ricercare le risorse per parola chiave.
* @param categoryCode Il codice della categoria delle risorse. null o
* stringa vuota nel caso non si voglia ricercare le risorse per categoria.
* @param groupCodes I codici dei gruppi consentiti tramite il quale
* filtrare le risorse.
* @return La lista di identificativi di risorse.
* @throws ApsSystemException In caso di errore.
*/
@Override
public List<String> searchResourcesId(String type, String text,
String categoryCode, Collection<String> groupCodes) throws ApsSystemException {
return this.searchResourcesId(type, text, null, categoryCode, groupCodes);
}
@Override
public List<String> searchResourcesId(String type, String text,
String filename, String categoryCode, Collection<String> groupCodes) throws ApsSystemException {
if (null == groupCodes || groupCodes.isEmpty()) {
return new ArrayList<String>();
}
List<String> resourcesId = null;
try {
resourcesId = this.getResourceDAO().searchResourcesId(type, text, filename, categoryCode, groupCodes);
} catch (Throwable t) {
_logger.error("Error searching resources id", t);
throw new ApsSystemException("Error searching resources id", t);
}
return resourcesId;
}
@Override
public List<String> searchResourcesId(FieldSearchFilter[] filters, String categoryCode, Collection<String> groupCodes) throws ApsSystemException {
this.checkFilterKeys(filters);
List<String> resourcesId = null;
try {
resourcesId = this.getResourceDAO().searchResourcesId(filters, categoryCode, groupCodes);
} catch (Throwable t) {
_logger.error("Error searching resources id", t);
throw new ApsSystemException("Error searching resources id", t);
}
return resourcesId;
}
protected void checkFilterKeys(FieldSearchFilter[] filters) {
if (null != filters && filters.length > 0) {
String[] allowedFilterKeys = {RESOURCE_ID_FILTER_KEY, RESOURCE_TYPE_FILTER_KEY, RESOURCE_DESCR_FILTER_KEY,
RESOURCE_MAIN_GROUP_FILTER_KEY, RESOURCE_FILENAME_FILTER_KEY, RESOURCE_CREATION_DATE_FILTER_KEY, RESOURCE_MODIFY_DATE_FILTER_KEY};
List<String> allowedFilterKeysList = Arrays.asList(allowedFilterKeys);
for (int i = 0; i < filters.length; i++) {
FieldSearchFilter filter = filters[i];
if (!allowedFilterKeysList.contains(filter.getKey())) {
throw new RuntimeException("Invalid filter key - '" + filter.getKey() + "'");
}
}
}
}
/**
* Restituisce la risorsa con l'id specificato.
* @param id L'identificativo della risorsa da caricare.
* @return La risorsa cercata. null se non vi è nessuna risorsa con l'identificativo immesso.
* @throws ApsSystemException in caso di errore.
*/
@Override
public ResourceInterface loadResource(String id) throws ApsSystemException {
ResourceInterface resource = null;
try {
ResourceRecordVO resourceVo = this.getResourceDAO().loadResourceVo(id);
if (null != resourceVo) {
resource = this.createResource(resourceVo);
resource.setMasterFileName(resourceVo.getMasterFileName());
}
} catch (Throwable t) {
_logger.error("Error loading resource : id {}", id, t);
throw new ApsSystemException("Error loading resource : id " + id, t);
}
return resource;
}
/**
* Metodo di servizio. Restituisce una risorsa
* in base ai dati del corrispondente record.
* @param resourceVo Il vo relativo al record.
* @return La risorsa valorizzata.
* @throws ApsSystemException in caso di errore.
*/
protected ResourceInterface createResource(ResourceRecordVO resourceVo) throws ApsSystemException {
String resourceType = resourceVo.getResourceType();
String resourceXML = resourceVo.getXml();
ResourceInterface resource = this.createResourceType(resourceType);
this.fillEmptyResourceFromXml(resource, resourceXML);
resource.setMainGroup(resourceVo.getMainGroup());
resource.setCreationDate(resourceVo.getCreationDate());
resource.setLastModified(resourceVo.getLastModified());
return resource;
}
/**
* Valorizza una risorsa prototipo con gli elementi
* dell'xml che rappresenta una risorsa specifica.
* @param resource Il prototipo di risorsa da specializzare con gli attributi dell'xml.
* @param xml L'xml della risorsa specifica.
* @throws ApsSystemException
*/
protected void fillEmptyResourceFromXml(ResourceInterface resource, String xml) throws ApsSystemException {
try {
SAXParserFactory parseFactory = SAXParserFactory.newInstance();
SAXParser parser = parseFactory.newSAXParser();
InputSource is = new InputSource(new StringReader(xml));
ResourceHandler handler = new ResourceHandler(resource, this.getCategoryManager());
parser.parse(is, handler);
} catch (Throwable t) {
_logger.error("Error loading resource", t);
throw new ApsSystemException("Error loading resource", t);
}
}
/**
* Cancella una risorsa dal db ed i file di ogni istanza dal filesystem.
* @param resource La risorsa da cancellare.
* @throws ApsSystemException in caso di errore nell'accesso al db.
*/
@Override
public void deleteResource(ResourceInterface resource) throws ApsSystemException {
try {
this.getResourceDAO().deleteResource(resource.getId());
resource.deleteResourceInstances();
} catch (Throwable t) {
_logger.error("Error deleting resource", t);
throw new ApsSystemException("Error deleting resource", t);
}
}
@Override
public void refreshMasterFileNames() throws ApsSystemException {
this.startResourceReloaderThread(null, ResourceReloaderThread.RELOAD_MASTER_FILE_NAME);
}
@Override
public void refreshResourcesInstances(String resourceTypeCode) throws ApsSystemException {
this.startResourceReloaderThread(resourceTypeCode, ResourceReloaderThread.REFRESH_INSTANCE);
}
protected void startResourceReloaderThread(String resourceTypeCode, int operationCode) throws ApsSystemException {
if (this.getStatus() != STATUS_READY) {
_logger.info("Service not ready : status {}", this.getStatus());
return;
}
String threadName = this.getName() + "_resourceReloader_" + DateConverter.getFormattedDate(new Date(), "yyyyMMdd");
try {
List<String> resources = this.getResourceDAO().searchResourcesId(resourceTypeCode, null, null, null);
ResourceReloaderThread thread = new ResourceReloaderThread(this, operationCode, resources);
thread.setName(threadName);
thread.start();
_logger.info("Reloader started");
} catch (Throwable t) {
_logger.error("Error refreshing Resource of type {} - Thread Name '{}'",resourceTypeCode, threadName, t);
}
}
protected void refreshMasterFileNames(String resourceId) {
try {
ResourceInterface resource = this.loadResource(resourceId);
if (resource.isMultiInstance()) {
ResourceInstance instance =
((AbstractMultiInstanceResource) resource).getInstance(0, null);
String filename = instance.getFileName();
int index = filename.lastIndexOf("_d0.");
String masterFileName = filename.substring(0, index) + filename.substring(index+3);
resource.setMasterFileName(masterFileName);
} else {
ResourceInstance instance =
((AbstractMonoInstanceResource) resource).getInstance();
resource.setMasterFileName(instance.getFileName());
}
this.updateResource(resource);
} catch (Throwable t) {
_logger.error("Error reloading master file name of resource {}", resourceId, t);
}
}
protected void refreshResourceInstances(String resourceId) {
try {
ResourceInterface resource = this.loadResource(resourceId);
resource.reloadResourceInstances();
this.updateResource(resource);
} catch (Throwable t) {
_logger.error("Error refreshing resource instances of resource {}", resourceId, t);
}
}
@Override
public List getGroupUtilizers(String groupName) throws ApsSystemException {
List<String> resourcesId = null;
try {
List<String> allowedGroups = new ArrayList<String>(1);
allowedGroups.add(groupName);
resourcesId = this.getResourceDAO().searchResourcesId(null, null, null, null, allowedGroups);
} catch (Throwable t) {
_logger.error("Error searching group utilizers : group '{}'", groupName, t);
throw new ApsSystemException("Error searching group utilizers : group '" + groupName + "'", t);
}
return resourcesId;
}
@Override
public List getCategoryUtilizers(String categoryCode) throws ApsSystemException {
List<String> resourcesId = null;
try {
resourcesId = this.getResourceDAO().searchResourcesId(null, null, null, categoryCode, null);
} catch (Throwable t) {
_logger.error("Error searching category utilizers : category code '{}'", categoryCode, t);
throw new ApsSystemException("Error searching category utilizers : category code '" + categoryCode + "'", t);
}
return resourcesId;
}
@Override
public void reloadCategoryReferences(String categoryCode) throws ApsSystemException {
try {
List<String> resources = this.getCategoryUtilizersForReloadReferences(categoryCode);
_logger.info("start reload category references for {} resources", resources.size());
ReloadingCategoryReferencesThread th = null;
Thread currentThread = Thread.currentThread();
if (currentThread instanceof ReloadingCategoryReferencesThread) {
th = (ReloadingCategoryReferencesThread) Thread.currentThread();
th.setListSize(resources.size());
}
if (null != resources && !resources.isEmpty()) {
Iterator<String> it = resources.iterator();
while (it.hasNext()) {
String code = it.next();
ResourceInterface resource = this.loadResource(code);
this.getResourceDAO().updateResourceRelations(resource);
if (null != th) th.setListIndex(th.getListIndex() + 1);
}
}
} catch (Throwable t) {
_logger.error("Error searching category utilizers : category code '{}'", categoryCode, t);
throw new ApsSystemException("Error searching category utilizers : category code '" + categoryCode + "'", t);
}
}
@Override
public List getCategoryUtilizersForReloadReferences(String categoryCode) throws ApsSystemException {
List<String> resourcesId = null;
try {
resourcesId = this.getCategoryUtilizers(categoryCode);
} catch (Throwable t) {
throw new ApsSystemException("Error searching category utilizers : category code '" + categoryCode + "'", t);
}
return resourcesId;
}
@Override
public int getStatus() {
return this._status;
}
protected void setStatus(int status) {
this._status = status;
}
/**
* Restutuisce il dao in uso al manager.
* @return Il dao in uso al manager.
*/
protected IResourceDAO getResourceDAO() {
return _resourceDao;
}
/**
* Setta il dao in uso al manager.
* @param resourceDao Il dao in uso al manager.
*/
public void setResourceDAO(IResourceDAO resourceDao) {
this._resourceDao = resourceDao;
}
public void setResourceTypes(Map<String, ResourceInterface> resourceTypes) {
this._resourceTypes = resourceTypes;
}
protected ICategoryManager getCategoryManager() {
return _categoryManager;
}
public void setCategoryManager(ICategoryManager categoryManager) {
this._categoryManager = categoryManager;
}
/**
* Mappa dei prototipi dei tipi di risorsa
*/
private Map<String, ResourceInterface> _resourceTypes;
private int _status;
private IResourceDAO _resourceDao;
private ICategoryManager _categoryManager;
}