View Javadoc
1   /*
2    * Copyright (c) 2002-2018, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  
35  package fr.paris.lutece.plugins.easyrulesbot.service.yaml;
36  
37  import fr.paris.lutece.plugins.easyrulesbot.service.yaml.model.YamlFilter;
38  import fr.paris.lutece.plugins.easyrulesbot.service.yaml.model.YamlBot;
39  import fr.paris.lutece.plugins.easyrulesbot.service.yaml.model.YamlRule;
40  import fr.paris.lutece.plugins.easyrulesbot.service.yaml.model.YamlCondition;
41  import com.fasterxml.jackson.databind.ObjectMapper;
42  import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
43  import fr.paris.lutece.plugins.chatbot.service.BotService;
44  import fr.paris.lutece.plugins.easyrulesbot.service.bot.rules.BotRule;
45  import fr.paris.lutece.plugins.easyrulesbot.service.bot.rules.conditions.Condition;
46  import fr.paris.lutece.plugins.easyrulesbot.service.bot.rules.conditions.ConditionsService;
47  import fr.paris.lutece.plugins.easyrulesbot.service.bot.EasyRulesBot;
48  import fr.paris.lutece.plugins.easyrulesbot.service.response.filters.FiltersService;
49  import fr.paris.lutece.plugins.easyrulesbot.service.response.filters.ResponseFilter;
50  import fr.paris.lutece.plugins.easyrulesbot.service.response.processors.ProcessorsService;
51  import fr.paris.lutece.plugins.easyrulesbot.service.response.processors.ResponseProcessor;
52  import fr.paris.lutece.portal.service.util.AppLogService;
53  import fr.paris.lutece.portal.service.util.AppPathService;
54  import java.io.File;
55  import java.io.FilenameFilter;
56  import java.io.IOException;
57  import java.util.ArrayList;
58  import java.util.List;
59  import org.apache.commons.io.FileUtils;
60  import org.easyrules.api.RulesEngine;
61  import org.easyrules.spring.RulesEngineFactoryBean;
62  
63  /**
64   * YamlBotLoader
65   */
66  public class YamlBotLoader
67  {
68      private static final String PATH_BOTS = "/WEB-INF/plugins/easyrulesbot/bots";
69      private static RulesEngineFactoryBean _factory;
70  
71      public static void loadBots( )
72      {
73          String strBotPath = AppPathService.getAbsolutePathFromRelativePath( PATH_BOTS );
74          File fBotPath = new File( strBotPath );
75  
76          File [ ] files = fBotPath.listFiles( new YamlFilenameFilter( ) );
77          AppLogService.debug( "Loading YAML bots ... " );
78          for ( File file : files )
79          {
80              try
81              {
82                  String strYaml = FileUtils.readFileToString( file, "UTF-8" );
83                  YamlBot yBot = loadYamlBot( strYaml );
84                  EasyRulesBot bot = createBot( yBot );
85                  BotService.register( bot );
86                  AppLogService.debug( "New YAML bot registered : " + yBot.getName( ) );
87              }
88              catch( IOException | YamlBotLoadingException ex )
89              {
90                  AppLogService.error( "Unable to load bot. Error : " + ex.getMessage( ), ex );
91              }
92          }
93      }
94  
95      public static YamlBot loadYamlBot( String strYaml ) throws IOException
96      {
97          ObjectMapper mapper = new ObjectMapper( new YAMLFactory( ) );
98  
99          YamlBot bot = mapper.readValue( strYaml, YamlBot.class );
100 
101         AppLogService.debug( "Loaded bot : " + bot );
102         return bot;
103     }
104 
105     /**
106      * Create a bot from a YAML decription of the bot
107      * 
108      * @param yamlBot
109      *            The YAML decription of the bot
110      * @return The bot
111      * @throws YamlBotLoadingException
112      *             if the loading fails
113      */
114     public static EasyRulesBot createBot( YamlBot yamlBot ) throws YamlBotLoadingException
115     {
116         EasyRulesBot bot = new EasyRulesBot( );
117         bot.setKey( yamlBot.getKey( ) );
118         bot.setName( yamlBot.getName( ) );
119         bot.setDescription( yamlBot.getDescription( ) );
120         bot.setWelcomeMessage( yamlBot.getWelcomeMessage( ) );
121         bot.setAvatarUrl( yamlBot.getAvatarUrl( ) );
122         bot.setStandalone( yamlBot.getStandalone( ) );
123         List<String> listLanguages = new ArrayList<String>();
124         listLanguages.add( yamlBot.getLanguage() );
125         bot.setListAvailableLanguages( listLanguages );
126 
127         // Get an engine and register rules
128         RulesEngine engine = getEngine( );
129         for ( YamlRule yamlRule : yamlBot.getRules( ) )
130         {
131             BotRule rule = new BotRule( );
132             rule.setName( yamlRule.getRule( ) );
133             rule.setDescription( yamlRule.getDescription( ) );
134             rule.setPriority( yamlRule.getPriority( ) );
135             rule.setMessageTemplate( yamlRule.getMessage( ) );
136             rule.setDataKey( yamlRule.getDataKey( ) );
137             ResponseProcessor processor = ProcessorsService.getProcessor( yamlRule.getProcessor( ) );
138             if ( processor == null )
139             {
140                 throw new YamlBotLoadingException( "Failed to create bot : unable to find processor " + yamlRule.getProcessor( ) );
141             }
142             rule.setResponseProcessor( processor );
143             rule.setResponseCommentTemplate( yamlRule.getResponseComment( ) );
144             List<Condition> listConditions = new ArrayList<>( );
145             if( yamlRule.getConditions( ) != null )
146             {
147                 for ( YamlCondition yamlCondition : yamlRule.getConditions( ) )
148                 {
149                     Condition condition = ConditionsService.getCondition( yamlCondition.getCondition( ) );
150                     if ( condition == null )
151                     {
152                         throw new YamlBotLoadingException( "Failed to create bot : unable to find condition " + yamlCondition.getCondition( ) );
153                     }
154                     condition.setParameters( yamlCondition.getParameters() );
155                     listConditions.add( condition );
156                 }
157             }
158             rule.setListConditions( listConditions );
159             rule.setButtons( yamlRule.getButtons() );
160             engine.registerRule( rule );
161         }
162         bot.setRulesEngine( engine );
163 
164         List<ResponseFilter> listFilters = new ArrayList<>( );
165         for ( YamlFilter yamlFilter : yamlBot.getFilters( ) )
166         {
167             ResponseFilter filter = FiltersService.getFilter( yamlFilter.getFilter( ) );
168             if ( filter == null )
169             {
170                 throw new YamlBotLoadingException( "Failed to create bot : unable to find filter " + yamlFilter.getFilter( ) );
171             }
172             listFilters.add( filter );
173         }
174         bot.setListResponseFilters( listFilters );
175         
176         AppLogService.debug( "Loaded bot : " + bot );
177 
178         return bot;
179     }
180 
181     private static RulesEngine getEngine( )
182     {
183         if ( _factory == null )
184         {
185             _factory = new RulesEngineFactoryBean( );
186             _factory.setSkipOnFirstAppliedRule( true );
187             _factory.setSkipOnFirstFailedRule( true );
188             _factory.setPriorityThreshold( 100 );
189             _factory.setSilentMode( false );
190         }
191         return _factory.getObject( );
192     }
193 
194     private static class YamlFilenameFilter implements FilenameFilter
195     {
196         @Override
197         public boolean accept( File dir, String filename )
198         {
199             return filename.endsWith( ".yml" ) || filename.endsWith( ".yaml" );
200         }
201     }
202 }