/**
* Copyright 2015-2017 Linagora, Université Joseph Fourier, Floralis
*
* The present code is developed in the scope of the joint LINAGORA -
* Université Joseph Fourier - Floralis research program and is designated
* as a "Result" pursuant to the terms and conditions of the LINAGORA
* - Université Joseph Fourier - Floralis research program. Each copyright
* holder of Results enumerated here above fully & independently holds complete
* ownership of the complete Intellectual Property rights applicable to the whole
* of said Results, and may freely exploit it in any manner which does not infringe
* the moral rights of the other copyright holders.
*
* 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 net.roboconf.dm.templating.internal;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collection;
import java.util.Collections;
import java.util.logging.Logger;
import net.roboconf.core.model.beans.Application;
import net.roboconf.core.model.beans.ApplicationTemplate;
import net.roboconf.core.model.beans.Instance;
import net.roboconf.core.model.runtime.EventType;
import net.roboconf.core.utils.Utils;
import net.roboconf.dm.management.ManagedApplication;
import net.roboconf.dm.management.Manager;
import net.roboconf.dm.management.events.IDmListener;
import net.roboconf.dm.templating.internal.templates.TemplateEntry;
import net.roboconf.dm.templating.internal.templates.TemplateUtils;
import net.roboconf.dm.templating.internal.templates.TemplateWatcher;
/**
* @author Pierre Bourret - Université Joseph Fourier
* @author Vincent Zurczak - Linagora
*/
public class TemplatingManager implements IDmListener {
public static final String ID = "DM's Templating";
// Locks
private final Object watcherLock = new Object();
private final Object generationLock = new Object();
// Injected by iPojo
private long pollInterval;
Manager dm;
// Fields
private final Logger logger = Logger.getLogger( getClass().getName());
File templatesDIR, outputDIR;
TemplateWatcher templateWatcher;
// Configuration
/**
* Sets the poll interval for the template watcher.
* @param pollInterval the poll interval, in milliseconds, for the template watcher
*/
public void setPollInterval( long pollInterval ) {
this.pollInterval = pollInterval;
this.logger.fine( "Template watcher poll interval set to " + pollInterval );
synchronized( this.watcherLock ) {
if( this.templateWatcher != null )
resetWatcher();
}
}
/**
* Sets the templates directory.
* @param templatesDirectory the templates directory
*/
public void setTemplatesDirectory( String templatesDirectory ) {
this.logger.fine( "Templates directory is now... " + templatesDirectory );
this.templatesDIR = Utils.isEmptyOrWhitespaces( templatesDirectory ) ? null : new File( templatesDirectory );
synchronized( this.watcherLock ) {
if( this.templateWatcher != null )
resetWatcher();
}
}
/**
* Sets the output directory.
* @param outputDirectory the output directory
*/
public void setOutputDirectory( String outputDirectory ) {
// Update the configuration
this.logger.fine( "Output directory is now... " + outputDirectory );
this.outputDIR = Utils.isEmptyOrWhitespaces( outputDirectory ) ? null : new File( outputDirectory );
// Generate the files
if( this.outputDIR != null && this.dm != null ) {
for( ManagedApplication ma : this.dm.applicationMngr().getManagedApplications())
application( ma.getApplication(), EventType.CHANGED );
}
}
/**
* Binds the DM.
* @param dm
*/
public void bindManager( Manager dm ) {
synchronized( this.generationLock ) {
this.logger.fine( "The DM is now available in the templating manager." );
this.dm = dm;
}
}
/**
* Unbinds the DM.
* @param dm
*/
public void unbindManager( Manager dm ) {
synchronized( this.generationLock ) {
this.logger.fine( "The DM is NOT available anymore in the templating manager." );
this.dm = null;
}
}
// iPojo life cycle
/**
* Starts the templating manager (invoked by iPojo).
*/
public void start() {
synchronized( this.watcherLock ) {
this.logger.config( "The templating manager is starting..." );
resetWatcher();
}
}
/**
* Stops the templating manager (invoked by iPojo).
*/
public void stop() {
synchronized( this.watcherLock ) {
this.logger.config( "The templating manager is stopping..." );
stopWatcher();
}
}
// Inherited methods
/*
* (non-Javadoc)
* @see net.roboconf.dm.management.events.IDmListener#getId()
*/
@Override
public String getId() {
return ID;
}
/* (non-Javadoc)
* @see net.roboconf.dm.management.events.IDmListener
* #application(net.roboconf.core.model.beans.Application, net.roboconf.dm.management.events.EventType)
*/
@Override
public void application( Application application, EventType eventType ) {
if( this.outputDIR == null ) {
this.logger.warning( "Generation from templates is skipped. Invalid output directory." );
} else if( eventType == EventType.DELETED ) {
synchronized( this.generationLock ) {
TemplateUtils.deleteGeneratedFiles( application, this.outputDIR );
}
} else {
generate( application );
}
}
/* (non-Javadoc)
* @see net.roboconf.dm.management.events.IDmListener
* #applicationTemplate(net.roboconf.core.model.beans.ApplicationTemplate, net.roboconf.dm.management.events.EventType)
*/
@Override
public void applicationTemplate( ApplicationTemplate tpl, EventType eventType ) {
// nothing
}
/*
* (non-Javadoc)
* @see net.roboconf.dm.management.events.IDmListener
* #instance(net.roboconf.core.model.beans.Instance, net.roboconf.core.model.beans.Application, net.roboconf.dm.management.events.EventType)
*/
@Override
public void instance( Instance instance, Application application, EventType eventType ) {
application( application, EventType.CHANGED );
}
/* (non-Javadoc)
* @see net.roboconf.dm.management.events.IDmListener
* #raw(java.lang.String, java.lang.Object[])
*/
@Override
public void raw( String message, Object... data ) {
// nothing
}
/*
* (non-Javadoc)
* @see net.roboconf.dm.management.events.IDmListener
* #enableNotifications()
*/
@Override
public void enableNotifications() {
// nothing
}
/*
* (non-Javadoc)
* @see net.roboconf.dm.management.events.IDmListener
* #disableNotifications()
*/
@Override
public void disableNotifications() {
// nothing
}
// Public methods
/**
* Processes new templates.
* <p>
* They are applied to all the applications, provided they match
* the criteria defined by the template files.
* </p>
*
* @param newTemplates a non-null list of templates
*/
public void processNewTemplates( Collection<TemplateEntry> newTemplates ) {
if( this.dm != null ) {
for( ManagedApplication ma : this.dm.applicationMngr().getManagedApplications()) {
Collection<TemplateEntry> filteredTemplates = TemplateUtils.findTemplatesForApplication( ma.getName(), newTemplates );
generate( ma.getApplication(), filteredTemplates );
}
}
}
// Private methods
/**
* Updates the template watcher after a change in the templating manager configuration.
*/
private void resetWatcher() {
// Stop the current watcher.
stopWatcher();
// Update the template & target directories, based on the provided configuration directory.
if( this.templatesDIR == null ) {
this.logger.warning( "The templates directory was not specified. DM templating is temporarily disabled." );
} else if( this.outputDIR == null ) {
this.logger.warning( "The templates directory was not specified. DM templating is temporarily disabled." );
} else {
this.templateWatcher = new TemplateWatcher( this, this.templatesDIR, this.pollInterval );
this.templateWatcher.start();
}
}
/**
* Stops the watcher, if any.
*/
private void stopWatcher() {
if( this.templateWatcher != null ) {
try {
this.templateWatcher.stop();
} finally {
this.templateWatcher = null;
}
}
}
/**
* Generates files from templates for a given application.
* <p>
* Templates are retrieved from the watcher.
* </p>
*
* @param application an application (not null)
*/
void generate( Application application ) {
// Get the templates
Collection<TemplateEntry> templates = Collections.emptyList();
synchronized( this.watcherLock ) {
if( this.templateWatcher != null )
templates = this.templateWatcher.findTemplatesForApplication( application.getName());
}
// Process them
generate( application, templates );
}
/**
* Generates files from templates for a given application.
* <p>
* Templates are provided in parameters.
* </p>
*
* @param application an application (not null)
* @param templates a non-null collection of templates
*/
void generate( Application application, Collection<TemplateEntry> templates ) {
try {
synchronized( this.generationLock ) {
TemplateUtils.generate( application, this.outputDIR, templates, this.logger );
}
} catch( IOException e ) {
this.logger.warning( "An error occurred while generating files from templates for application " + application );
Utils.logException( this.logger, e );
} catch( Throwable e ) {
this.logger.warning( "An unexpected error occurred while generating files from templates for application " + application );
Utils.logException( this.logger, new UndeclaredThrowableException( e ));
}
}
}