org.brekka.pegasus.core.services.impl.TemplateServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.brekka.pegasus.core.services.impl.TemplateServiceImpl.java

Source

/*
 * Copyright 2013 the original author or authors.
 *
 * 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.brekka.pegasus.core.services.impl;

import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.brekka.commons.persistence.model.ListingCriteria;
import org.brekka.commons.persistence.model.OrderByPart;
import org.brekka.commons.persistence.model.OrderByProperty;
import org.brekka.pegasus.core.PegasusErrorCode;
import org.brekka.pegasus.core.PegasusException;
import org.brekka.pegasus.core.dao.TemplateDAO;
import org.brekka.pegasus.core.model.KeySafe;
import org.brekka.pegasus.core.model.Template;
import org.brekka.pegasus.core.model.TemplateEngine;
import org.brekka.pegasus.core.model.Token;
import org.brekka.pegasus.core.model.XmlEntity;
import org.brekka.pegasus.core.services.TemplateService;
import org.brekka.pegasus.core.services.XmlEntityService;
import org.brekka.xml.pegasus.v2.model.ExportedTemplateType;
import org.brekka.xml.pegasus.v2.model.ExportedTemplatesDocument;
import org.brekka.xml.pegasus.v2.model.ExportedTemplatesDocument.ExportedTemplates;
import org.brekka.xml.pegasus.v2.model.TemplateDocument;
import org.brekka.xml.pegasus.v2.model.TemplateType;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ClassUtils;

/**
 * Standard implementation of the template service.
 *
 * @author Andrew Taylor (andrew@brekka.org)
 */
@Service
@Transactional
public class TemplateServiceImpl implements TemplateService {

    @Autowired
    private TemplateDAO templateDAO;

    @Autowired
    private XmlEntityService xmlEntityService;

    public Map<TemplateEngine, TemplateEngineAdapter> adapters;

    @PostConstruct
    public void init() {
        if (this.adapters != null) {
            // externally configured
            return;
        }
        Map<TemplateEngine, TemplateEngineAdapter> templateAdapters = new LinkedHashMap<>();
        if (ClassUtils.isPresent("org.apache.velocity.app.VelocityEngine", getClass().getClassLoader())) {
            // Velocity is present, add it
            TemplateEngineAdapter templateEngineAdapter = new org.brekka.pegasus.core.services.impl.VelocityTemplateEngine();
            templateEngineAdapter.init(this.templateDAO, this.xmlEntityService);
            templateAdapters.put(TemplateEngine.VELOCITY, templateEngineAdapter);
        }
        this.adapters = templateAdapters;
    }

    @Override
    @Nullable
    public String merge(@Nonnull final Template template, @Nonnull final Map<String, Object> context) {
        StringWriter writer = new StringWriter();
        merge(template, context, writer);
        return writer.toString();
    }

    @Override
    public void merge(final Template template, final Map<String, Object> context, final Writer out) {
        TemplateEngineAdapter templateEngineAdapter = this.adapters.get(template.getEngine());
        if (templateEngineAdapter != null) {
            templateEngineAdapter.merge(template, context, out);
        } else {
            throw new PegasusException(PegasusErrorCode.PG773, "No logic configured for engine '%s'",
                    template.getEngine());
        }
    }

    @Override
    @Nullable
    public String preview(final String templateContent, final TemplateEngine templateEngine,
            final Map<String, Object> context) {
        StringWriter writer = new StringWriter();
        preview(templateContent, templateEngine, context, writer);
        return writer.toString();
    }

    @Override
    public void preview(final String templateContent, final TemplateEngine templateEngine,
            final Map<String, Object> context, final Writer out) {
        TemplateEngineAdapter templateEngineAdapter = this.adapters.get(templateEngine);
        if (templateEngineAdapter != null) {
            templateEngineAdapter.preview(templateContent, context, out);
        } else {
            throw new PegasusException(PegasusErrorCode.PG773, "No logic configured for engine '%s'",
                    templateEngine);
        }
    }

    @Override
    @Nullable
    @Transactional(readOnly = true)
    public Template retrieveByToken(@Nonnull final Token token) {
        return this.templateDAO.retrieveByToken(token);
    }

    @Override
    @Nullable
    @Transactional(readOnly = true)
    public Template retrieveBySlug(@Nonnull final String slug) {
        return this.templateDAO.retrieveBySlug(slug);
    }

    @Override
    @Nullable
    @Transactional(readOnly = true)
    public Template retrieveById(@Nonnull final UUID templateId) {
        return this.templateDAO.retrieveById(templateId);
    }

    @Override
    @Nonnull
    @Transactional()
    public Template create(@Nonnull final TemplateType details, @Nonnull final TemplateEngine engine,
            @Nullable final KeySafe<?> keySafe, @Nullable final String slug, @Nullable final Token token,
            @Nullable final String label) {
        return create(details, engine, keySafe, slug, token, label, false);
    }

    @Override
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void update(@Nonnull final Template template) {
        Template managed = this.templateDAO.retrieveById(template.getId());
        fixDetails(template.getXml().getBean().getTemplate());
        XmlEntity<TemplateDocument> incoming = template.getXml();
        XmlEntity<TemplateDocument> current = managed.getXml();
        XmlEntity<TemplateDocument> xml = this.xmlEntityService.updateEntity(incoming, current,
                TemplateDocument.class);
        managed.setXml(xml);
        // Allow the caller to update the slug/token.
        managed.setSlug(template.getSlug());
        managed.setToken(template.getToken());
        managed.setEngine(template.getEngine());
        managed.setLabel(template.getLabel());
        managed.setImported(template.getImported());

        this.templateDAO.update(managed);
        template.setXml(xml);
    }

    @Override
    @Transactional()
    public void delete(final UUID templateId) {
        this.templateDAO.delete(templateId);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Template> retrieveListing(final ListingCriteria listingCriteria) {
        return this.templateDAO.retrieveListing(null, listingCriteria);
    }

    @Override
    @Transactional(readOnly = true)
    public int retrieveListingRowCount() {
        return this.templateDAO.retrieveListingRowCount();
    }

    @Override
    @Transactional(readOnly = true)
    public ExportedTemplatesDocument exportAll(final DateTime changedSince) {
        int count = this.templateDAO.retrieveListingRowCount();
        List<Template> listing = this.templateDAO.retrieveListing(changedSince,
                new ListingCriteria(0, count, Arrays.<OrderByPart>asList(new OrderByProperty("created", false))));
        ExportedTemplatesDocument doc = ExportedTemplatesDocument.Factory.newInstance();
        ExportedTemplates templates = doc.addNewExportedTemplates();
        for (Template template : listing) {
            this.xmlEntityService.release(template, TemplateDocument.class);
            TemplateType templateXml = template.getXml().getBean().getTemplate();
            ExportedTemplateType exportedTemplate = templates.addNewExportedTemplate();
            exportedTemplate.setSlug(template.getSlug());
            exportedTemplate.setPlainLabel(template.getLabel());
            exportedTemplate.setLabel(templateXml.getLabel());
            exportedTemplate.setContent(templateXml.getContent());
            exportedTemplate.setContentType(templateXml.getContentType());
            exportedTemplate.setDocumentation(templateXml.getDocumentation());
            exportedTemplate.setEngine(template.getEngine().toString());
            exportedTemplate.setExampleVariables(templateXml.getExampleVariables());
            exportedTemplate.setEncrypt(template.getXml().getCryptedDataId() != null);
        }
        return doc;
    }

    @Override
    @Transactional()
    public int importFrom(final ExportedTemplatesDocument exportedTemplatesDocument, final KeySafe<?> keySafe,
            final boolean forceUpdate) {
        int count = 0;
        ExportedTemplates exportedTemplates = exportedTemplatesDocument.getExportedTemplates();
        List<ExportedTemplateType> exportedTemplateList = exportedTemplates.getExportedTemplateList();
        for (ExportedTemplateType exportedTemplateType : exportedTemplateList) {
            String slug = exportedTemplateType.getSlug();
            TemplateType template = TemplateType.Factory.newInstance();
            template.setLabel(exportedTemplateType.getLabel());
            template.setContent(exportedTemplateType.getContent());
            template.setDocumentation(exportedTemplateType.getDocumentation());
            template.setExampleVariables(exportedTemplateType.getExampleVariables());
            TemplateEngine templateEngine = TemplateEngine.valueOf(exportedTemplateType.getEngine());
            Template existing = this.templateDAO.retrieveBySlug(slug);
            if (existing == null) {
                KeySafe<?> keySafeForCreate = (exportedTemplateType.getEncrypt() ? keySafe : null);
                create(template, templateEngine, keySafeForCreate, slug, null, exportedTemplateType.getPlainLabel(),
                        true);
                count++;
            } else if (BooleanUtils.isTrue(existing.getImported()) || forceUpdate) {
                // The template was originally imported or force has been authorized, we can update it.
                if (existing.getXml().getCryptedDataId() != null && !forceUpdate) {
                    // Difficult to know at this point whether we can decrypt the XML. Really need a
                    // XmlEntityService release method that will not throw an exception and rollback our transaction.
                    // Assume 'forceUpdate' ignores this.
                    continue;
                }
                existing.setEngine(templateEngine);
                existing.setLabel(exportedTemplateType.getLabel());
                // If we force updated, treat it as not automatic import.
                existing.setImported(Boolean.valueOf(!forceUpdate));
                this.xmlEntityService.release(existing, TemplateDocument.class);
                XmlEntity<TemplateDocument> xml = existing.getXml();
                TemplateType newXml = xml.getBean().getTemplate();
                newXml.setContent(exportedTemplateType.getContent());
                if (exportedTemplateType.isSetContentType()) {
                    newXml.setContentType(exportedTemplateType.getContentType());
                }
                if (exportedTemplateType.isSetDocumentation()) {
                    newXml.setDocumentation(exportedTemplateType.getDocumentation());
                }
                newXml.setExampleVariables(exportedTemplateType.getExampleVariables());
                newXml.setLabel(exportedTemplateType.getLabel());
                update(existing);
                if (xml.getVersion() != existing.getXml().getVersion()) {
                    count++;
                }
            }
        }
        return count;
    }

    @Override
    public Set<TemplateEngine> getAvailableEngines() {
        Set<TemplateEngine> engines = EnumSet.copyOf(this.adapters.keySet());
        return engines;
    }

    protected Template create(@Nonnull final TemplateType details, @Nonnull final TemplateEngine engine,
            @Nullable final KeySafe<?> keySafe, @Nullable final String slug, @Nullable final Token token,
            @Nullable final String label, final boolean imported) {
        Template template = new Template();

        fixDetails(details);
        TemplateDocument templateDocument = TemplateDocument.Factory.newInstance();
        templateDocument.setTemplate(details);

        XmlEntity<TemplateDocument> xml;
        if (keySafe == null) {
            xml = this.xmlEntityService.persistPlainEntity(templateDocument, false);
        } else {
            xml = this.xmlEntityService.persistEncryptedEntity(templateDocument, keySafe, false);
        }
        template.setEngine(engine);
        template.setSlug(slug);
        template.setToken(token);
        template.setXml(xml);
        template.setLabel(label);
        template.setImported(imported);
        this.templateDAO.create(template);
        return template;
    }

    @Override
    @Transactional
    public void applyEncryption(final UUID templateId, final KeySafe<?> keySafe) {
        Template managed = this.templateDAO.retrieveById(templateId);
        if (managed.getXml().getCryptedDataId() != null) {
            // Already encrypted
            return;
        }
        XmlEntity<TemplateDocument> xml = this.xmlEntityService.applyEncryption(managed.getXml(), keySafe,
                TemplateDocument.class);
        managed.setXml(xml);
        this.templateDAO.update(managed);
    }

    @Override
    @Transactional
    public void removeEncryption(final UUID templateId) {
        Template managed = this.templateDAO.retrieveById(templateId);
        if (managed.getXml().getCryptedDataId() == null) {
            // Already plain
            return;
        }
        XmlEntity<TemplateDocument> xml = this.xmlEntityService.removeEncryption(managed.getXml(),
                TemplateDocument.class);
        managed.setXml(xml);
        this.templateDAO.update(managed);
    }

    protected void fixDetails(final TemplateType details) {
        if (details == null) {
            return;
        }
        if (details.isSetDocumentation() && details.getDocumentation() == null) {
            details.unsetDocumentation();
        }
        if (details.getContent() == null) {
            details.setContent(StringUtils.EMPTY);
        }
    }

    /**
     * @param adapters the adapters to set
     */
    public void setAdapters(final Map<TemplateEngine, TemplateEngineAdapter> adapters) {
        this.adapters = adapters;
    }
}