/*
* * This file is part of the Hesperides distribution.
* * (https://github.com/voyages-sncf-technologies/hesperides)
* * Copyright (c) 2016 VSCT.
* *
* * Hesperides 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, version 3.
* *
* * Hesperides 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 com.vsct.dt.hesperides.templating.packages;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.vsct.dt.hesperides.exception.runtime.DuplicateResourceException;
import com.vsct.dt.hesperides.storage.EventStore;
import com.vsct.dt.hesperides.storage.AbstractThreadAggregate;
import com.vsct.dt.hesperides.storage.UserProvider;
import com.vsct.dt.hesperides.templating.models.HesperidesPropertiesModel;
import com.vsct.dt.hesperides.templating.models.Models;
import com.vsct.dt.hesperides.templating.modules.template.Template;
import com.vsct.dt.hesperides.templating.modules.template.TemplateData;
import com.vsct.dt.hesperides.templating.modules.template.TemplateRegistryInterface;
import com.vsct.dt.hesperides.templating.packages.cache.TemplatePackageStoragePrefixInterface;
import com.vsct.dt.hesperides.templating.packages.event.*;
import com.vsct.dt.hesperides.util.HesperidesVersion;
import com.vsct.dt.hesperides.util.Release;
import com.vsct.dt.hesperides.util.WorkingCopy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
/**
* Service used to manage templates as "packs".
* There is no object representing the pack.
* Templates belong to the same "TemplatePackage" through namespacing
* Created by william_montaz on 24/11/2014.
*/
public abstract class AbstractTemplatePackagesAggregate extends AbstractThreadAggregate
implements TemplatePackages, TemplatePackageEventInterface, TemplatePackageStoragePrefixInterface {
/**
* Logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTemplatePackagesAggregate.class);
/**
* Name of Aggregate
*/
protected static final String NAME = "TemplatePackages";
/**
* Constructor using no UserProvider (used when no loggin was possible)
* @param eventBus The {@link EventBus} used to propagate events to other part of the application
* @param eventStore The {@link EventStore} used to store events
*/
public AbstractTemplatePackagesAggregate(final EventBus eventBus, final EventStore eventStore) {
super(eventBus, eventStore);
}
/**
* Constructor using a specific UserProvider
* @param eventBus The {@link EventBus} used to propagate events to other part of the application
* @param eventStore The {@link EventStore} used to store events
* @param userProvider A {@link UserProvider} that indicates which user is performing the request
*/
public AbstractTemplatePackagesAggregate(final EventBus eventBus, final EventStore eventStore,
final UserProvider userProvider) {
super(eventBus, eventStore, userProvider);
}
/**
* Helper method used to apply a treatment to all templates
* @param consumer A {@link Consumer} implementation to perform treatment on a template
*/
@Override
public void withAll(Consumer<Template> consumer) {
getTemplateRegistry().allTemplates().stream()
.forEach(valueObject -> consumer.accept(valueObject));
}
/**
* Get all templates for a template package
* @param packageKey The key describing the template package
* @return a {@link Set} of all templates belonging to the TemplatePackage
*/
@Override
public Set<Template> getAllTemplates(final TemplatePackageKey packageKey) {
return getTemplateRegistry().getAllTemplates(packageKey);
}
@Override
public Collection<Template> getAllTemplates() {
return getTemplateRegistry().allTemplates();
}
/**
* Get a template
* @param packageKey The {@link TemplatePackageKey} describing the template package
* @param templateName The name field of the template
* @return An {@link Optional} of a template
*/
@Override
public Optional<Template> getTemplate(final TemplatePackageKey packageKey, final String templateName) {
return getTemplateRegistry().getTemplate(packageKey.getNamespace(), templateName);
}
/**
* Get a template
* @param templateNamespace The namespace describing the tempalte package
* @param templateName The name field of the template
* @return An {@link Optional} of a template
*/
@Deprecated
@Override
public Optional<Template> getTemplate(String templateNamespace, String templateName) {
return getTemplateRegistry().getTemplate(templateNamespace, templateName);
}
/**
* Adds a new template to a working copy of a template package
* @param packageKey The {@link TemplatePackageKey} describing the template package. Whatever the key it will be transformed to a working copy.
* @param templateData The data held by the template -> should be replaced by templateVO
* @return The created {@link Template} with its versionID
*/
@Override
public Template createTemplateInWorkingCopy(final TemplatePackageWorkingCopyKey packageKey, final TemplateData templateData) {
return createTemplate(packageKey, templateData);
}
/**
* Creates a template in a template package (working copy or release)
* The template content has to be validated
* Fires a {@link TemplateCreatedEvent}
* Will throw a {@link DuplicateResourceException} if the template already exists
* @param packageKey The {@link TemplatePackageKey} describing the template package
* @param templateData The data held by the template -> should be replaced by templateVO
* @return The {@link Template} created with its new version id
*/
private Template createTemplate(final TemplatePackageKey packageKey, final TemplateData templateData) {
Template.validateContent(templateData.getContent());
final TemplateCreatedCommand hc = new TemplateCreatedCommand(getTemplateRegistry(), packageKey, templateData);
final TemplateCreatedEvent templateCreatedEvent = this.tryAtomic(packageKey.getEntityName(), hc);
return templateCreatedEvent.getCreated();
}
/**
* Updates a template in a working copy
* The content has to be validated first (is it mustache compliant ?)
* The version id provided must match the actual version id of the template.
* Fires a {@link TemplateUpdatedEvent}
* Will throw {@link com.vsct.dt.hesperides.exception.runtime.MissingResourceException} if the template does not exists
* @param packageKey The {@link TemplatePackageKey} describing the template package. Whatever the key it will be transformed to a working copy.
* @param templateData The data held by the template -> should be replaced by templateVO
* @return the updated {@link Template} with its new version id
*/
@Override
public Template updateTemplateInWorkingCopy(final TemplatePackageWorkingCopyKey packageKey, final TemplateData templateData) {
return updateTemplate(packageKey, templateData);
}
/**
* Updates a template in a template package
* The content has to be validated first (is it mustache compliant ?)
* The version id provided must match the actual version id of the template.
* Fires a {@link TemplateUpdatedEvent}
* Will throw {@link com.vsct.dt.hesperides.exception.runtime.MissingResourceException} if the template does not exists
* @param packageKey The {@link TemplatePackageKey} describing the template package.
* @param templateData The data held by the template -> should be replaced by templateVO
* @return The updated {@link Template} with its new version id
*/
private Template updateTemplate(final TemplatePackageKey packageKey, final TemplateData templateData) {
Template.validateContent(templateData.getContent());
final TemplateUpdatedCommand hc = new TemplateUpdatedCommand(getTemplateRegistry(), packageKey, templateData);
final TemplateUpdatedEvent templateUpdatedEvent = this.tryAtomic(packageKey.getEntityName(), hc);
return templateUpdatedEvent.getUpdated();
}
/**
* Deletes a template in a working copy
* Whatever package key is given it will be turned to a working copy
* Will throw {@link com.vsct.dt.hesperides.exception.runtime.MissingResourceException} if the template does not exist
* @param packageKey The {@link TemplatePackageKey} describing the template package
* @param templateName The name field of the template
*/
@Override
public void deleteTemplateInWorkingCopy(final TemplatePackageWorkingCopyKey packageKey, final String templateName) {
deleteTemplate(packageKey, templateName);
}
/**
* Deletes a template in a working copy
* Will throw {@link com.vsct.dt.hesperides.exception.runtime.MissingResourceException} if the template does not exist
* @param packageKey The {@link TemplatePackageKey} describing the template package
* @param templateName The name field of the template
*/
private void deleteTemplate(final TemplatePackageKey packageKey, final String templateName) {
final TemplateDeletedCommand hc = new TemplateDeletedCommand(getTemplateRegistry(), packageKey, templateName);
this.tryAtomic(packageKey.getEntityName(), hc);
}
/**
* Creates a release from a working copy
* @param workingCopyKey the {@link TemplatePackageWorkingCopyKey} of the working copy to create a release from
* @return The {@link TemplatePackageKey} of the created release
*/
@Override
public TemplatePackageKey createRelease(final TemplatePackageWorkingCopyKey workingCopyKey) {
final TemplatePackageKey releaseInfos = new TemplatePackageKey(
workingCopyKey.getName(),
Release.of(workingCopyKey.getVersion().getVersionName())
);
return createNewTemplatePackageFrom(releaseInfos, workingCopyKey);
}
/**
* Creates a working copy from another template package (working copy or release)
* @param workingCopyKey The {@link TemplatePackageWorkingCopyKey} of the working copy to create
* @param fromPackageKey The {@link TemplatePackageKey} of the template package to copy from
* @return The {@link TemplatePackageKey} of the created working copy
*/
@Override
public TemplatePackageKey createWorkingCopyFrom(final TemplatePackageWorkingCopyKey workingCopyKey, final TemplatePackageKey fromPackageKey) {
return createNewTemplatePackageFrom(workingCopyKey, fromPackageKey);
}
/**
* Creates a template package from another template package
* This function is not really atomic because a template might be change will getting the whole list,
* It is really not likely to happened
* Fires as many {@link TemplateCreatedEvent} as templates existing in the "from" template epackage
* @param newPackageInfo The {@link TemplatePackageKey} of the package to create
* @param fromPackageInfos The {@link TemplatePackageKey} of the package to create from
* @return The {@link TemplatePackageKey} of the new package
*/
private TemplatePackageKey createNewTemplatePackageFrom(final TemplatePackageKey newPackageInfo, final TemplatePackageKey fromPackageInfos) {
if (getTemplateRegistry().templateHasNamespace(newPackageInfo.getNamespace())) {
throw new DuplicateResourceException("Package " + newPackageInfo + "already exists.");
}
final Set<Template> fromTemplates = getTemplateRegistry().getAllTemplatesForNamespace(fromPackageInfos.getNamespace());
fromTemplates.forEach(template -> {
TemplateData templateData = TemplateData.withTemplateName(template.getName())
.withFilename(template.getFilename())
.withLocation(template.getLocation())
.withContent(template.getContent())
.withRights(template.getRights())
.build();
createTemplate(newPackageInfo, templateData);
});
//Package info is immutable, just return it
return newPackageInfo;
}
/**
* Deletes a complete template package ie. all templates related to it
* Actually, the event stream is preserved and a {@link TemplatePackageDeletedEvent} is fired
* @param packageKey The {@link TemplatePackageKey} to delete
*/
@Override
public void delete(final TemplatePackageKey packageKey){
final TemplatePackageDeletedCommand hc = new TemplatePackageDeletedCommand(getTemplateRegistry(), packageKey);
this.tryAtomic(packageKey.getEntityName(), hc);
}
/**
* Convenient method to get a template package model from the name, version and type
* It should be replaced in favor of a method using the key object
* @param name
* @param version
* @param isWorkingCopy
* @return The {@link HesperidesPropertiesModel} for the given template package key
*/
@Deprecated
@Override
public HesperidesPropertiesModel getModel(final String name, final String version, final boolean isWorkingCopy) {
final TemplatePackageKey packageKey = new TemplatePackageKey(name, version, isWorkingCopy);
return getModels().getPropertiesModel(packageKey.getNamespace());
}
@Subscribe
@Override
public void replayTemplateCreatedEvent(final TemplateCreatedEvent event) {
try {
final Template template = event.getCreated();
final String[] tokens = template.getNamespace().split("#");
final HesperidesVersion version = new HesperidesVersion(tokens[2], WorkingCopy.is(tokens[3]));
final TemplatePackageKey packageKey = new TemplatePackageKey(tokens[1], version);
final TemplateData templateData = TemplateData.withTemplateName(template.getName())
.withFilename(template.getFilename())
.withLocation(template.getLocation())
.withContent(template.getContent())
.withRights(template.getRights())
.build();
createTemplate(packageKey, templateData);
} catch (Exception e) {
LOGGER.error("Error while replaying template created event {}", e.getMessage());
}
}
@Subscribe
@Override
public void replayTemplateUpdatedEvent(final TemplateUpdatedEvent event) {
try {
final Template template = event.getUpdated();
final String[] tokens = template.getNamespace().split("#");
final HesperidesVersion version = new HesperidesVersion(tokens[2], WorkingCopy.is(tokens[3]));
final TemplatePackageKey packageKey = new TemplatePackageKey(tokens[1], version);
final TemplateData templateData = TemplateData.withTemplateName(template.getName())
.withFilename(template.getFilename())
.withLocation(template.getLocation())
.withContent(template.getContent())
.withRights(template.getRights())
.withVersionID(template.getVersionID() - 1)
.build();
this.updateTemplate(packageKey, templateData);
} catch (Exception e) {
LOGGER.error("Error while replaying tempalte updated event {}", e.getMessage());
}
}
@Subscribe
@Override
public void replayTemplateDeletedEvent(final TemplateDeletedEvent event) {
try {
String[] tokens = event.getNamespace().split("#");
final HesperidesVersion version = new HesperidesVersion(tokens[2], WorkingCopy.is(tokens[3]));
final TemplatePackageKey packageKey = new TemplatePackageKey(tokens[1], version);
AbstractTemplatePackagesAggregate.this.deleteTemplate(packageKey, event.getName());
} catch (Exception e) {
LOGGER.error("Error while replaying template deleted event {}", e.getMessage());
}
}
@Subscribe
@Override
public void replayTemplatePackageDeletedEvent(final TemplatePackageDeletedEvent event){
try{
final HesperidesVersion version = event.isWorkingCopy() ? WorkingCopy.of(event.getPackageVersion()) : Release.of(event.getPackageVersion());
final TemplatePackageKey packageKey = TemplatePackageKey.withName(event.getPackageName()).withVersion(version).build();
AbstractTemplatePackagesAggregate.this.delete(packageKey);
} catch (Exception e){
LOGGER.error("Error while replaying template package deleted event {}", e.getMessage());
}
}
/**
* Internal structure holding in memory state.
*
* @return template registry
*/
protected abstract TemplateRegistryInterface getTemplateRegistry();
/**
* Helper class used to return a template model.
*
* @return model
*/
protected abstract Models getModels();
}