YamlBotLoader.java
/*
* Copyright (c) 2002-2018, Mairie de Paris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* License 1.0
*/
package fr.paris.lutece.plugins.easyrulesbot.service.yaml;
import fr.paris.lutece.plugins.easyrulesbot.service.yaml.model.YamlFilter;
import fr.paris.lutece.plugins.easyrulesbot.service.yaml.model.YamlBot;
import fr.paris.lutece.plugins.easyrulesbot.service.yaml.model.YamlRule;
import fr.paris.lutece.plugins.easyrulesbot.service.yaml.model.YamlCondition;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import fr.paris.lutece.plugins.chatbot.service.BotService;
import fr.paris.lutece.plugins.easyrulesbot.service.bot.rules.BotRule;
import fr.paris.lutece.plugins.easyrulesbot.service.bot.rules.conditions.Condition;
import fr.paris.lutece.plugins.easyrulesbot.service.bot.rules.conditions.ConditionsService;
import fr.paris.lutece.plugins.easyrulesbot.service.bot.EasyRulesBot;
import fr.paris.lutece.plugins.easyrulesbot.service.response.filters.FiltersService;
import fr.paris.lutece.plugins.easyrulesbot.service.response.filters.ResponseFilter;
import fr.paris.lutece.plugins.easyrulesbot.service.response.processors.ProcessorsService;
import fr.paris.lutece.plugins.easyrulesbot.service.response.processors.ResponseProcessor;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPathService;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.easyrules.api.RulesEngine;
import org.easyrules.spring.RulesEngineFactoryBean;
/**
* YamlBotLoader
*/
public class YamlBotLoader
{
private static final String PATH_BOTS = "/WEB-INF/plugins/easyrulesbot/bots";
private static RulesEngineFactoryBean _factory;
public static void loadBots( )
{
String strBotPath = AppPathService.getAbsolutePathFromRelativePath( PATH_BOTS );
File fBotPath = new File( strBotPath );
File [ ] files = fBotPath.listFiles( new YamlFilenameFilter( ) );
AppLogService.debug( "Loading YAML bots ... " );
for ( File file : files )
{
try
{
String strYaml = FileUtils.readFileToString( file, "UTF-8" );
YamlBot yBot = loadYamlBot( strYaml );
EasyRulesBot bot = createBot( yBot );
BotService.register( bot );
AppLogService.debug( "New YAML bot registered : " + yBot.getName( ) );
}
catch( IOException | YamlBotLoadingException ex )
{
AppLogService.error( "Unable to load bot. Error : " + ex.getMessage( ), ex );
}
}
}
public static YamlBot loadYamlBot( String strYaml ) throws IOException
{
ObjectMapper mapper = new ObjectMapper( new YAMLFactory( ) );
YamlBot bot = mapper.readValue( strYaml, YamlBot.class );
AppLogService.debug( "Loaded bot : " + bot );
return bot;
}
/**
* Create a bot from a YAML decription of the bot
*
* @param yamlBot
* The YAML decription of the bot
* @return The bot
* @throws YamlBotLoadingException
* if the loading fails
*/
public static EasyRulesBot createBot( YamlBot yamlBot ) throws YamlBotLoadingException
{
EasyRulesBot bot = new EasyRulesBot( );
bot.setKey( yamlBot.getKey( ) );
bot.setName( yamlBot.getName( ) );
bot.setDescription( yamlBot.getDescription( ) );
bot.setWelcomeMessage( yamlBot.getWelcomeMessage( ) );
bot.setAvatarUrl( yamlBot.getAvatarUrl( ) );
bot.setStandalone( yamlBot.getStandalone( ) );
List<String> listLanguages = new ArrayList<String>();
listLanguages.add( yamlBot.getLanguage() );
bot.setListAvailableLanguages( listLanguages );
// Get an engine and register rules
RulesEngine engine = getEngine( );
for ( YamlRule yamlRule : yamlBot.getRules( ) )
{
BotRule rule = new BotRule( );
rule.setName( yamlRule.getRule( ) );
rule.setDescription( yamlRule.getDescription( ) );
rule.setPriority( yamlRule.getPriority( ) );
rule.setMessageTemplate( yamlRule.getMessage( ) );
rule.setDataKey( yamlRule.getDataKey( ) );
ResponseProcessor processor = ProcessorsService.getProcessor( yamlRule.getProcessor( ) );
if ( processor == null )
{
throw new YamlBotLoadingException( "Failed to create bot : unable to find processor " + yamlRule.getProcessor( ) );
}
rule.setResponseProcessor( processor );
rule.setResponseCommentTemplate( yamlRule.getResponseComment( ) );
List<Condition> listConditions = new ArrayList<>( );
if( yamlRule.getConditions( ) != null )
{
for ( YamlCondition yamlCondition : yamlRule.getConditions( ) )
{
Condition condition = ConditionsService.getCondition( yamlCondition.getCondition( ) );
if ( condition == null )
{
throw new YamlBotLoadingException( "Failed to create bot : unable to find condition " + yamlCondition.getCondition( ) );
}
condition.setParameters( yamlCondition.getParameters() );
listConditions.add( condition );
}
}
rule.setListConditions( listConditions );
rule.setButtons( yamlRule.getButtons() );
engine.registerRule( rule );
}
bot.setRulesEngine( engine );
List<ResponseFilter> listFilters = new ArrayList<>( );
for ( YamlFilter yamlFilter : yamlBot.getFilters( ) )
{
ResponseFilter filter = FiltersService.getFilter( yamlFilter.getFilter( ) );
if ( filter == null )
{
throw new YamlBotLoadingException( "Failed to create bot : unable to find filter " + yamlFilter.getFilter( ) );
}
listFilters.add( filter );
}
bot.setListResponseFilters( listFilters );
AppLogService.debug( "Loaded bot : " + bot );
return bot;
}
private static RulesEngine getEngine( )
{
if ( _factory == null )
{
_factory = new RulesEngineFactoryBean( );
_factory.setSkipOnFirstAppliedRule( true );
_factory.setSkipOnFirstFailedRule( true );
_factory.setPriorityThreshold( 100 );
_factory.setSilentMode( false );
}
return _factory.getObject( );
}
private static class YamlFilenameFilter implements FilenameFilter
{
@Override
public boolean accept( File dir, String filename )
{
return filename.endsWith( ".yml" ) || filename.endsWith( ".yaml" );
}
}
}