View Javadoc
1   /*
2    * Copyright (C) 2003-2007 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.cms.scripts.impl;
18  
19  import groovy.lang.GroovyClassLoader;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.File;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Set;
29  
30  import javax.jcr.Node;
31  import javax.jcr.NodeIterator;
32  import javax.jcr.Property;
33  import javax.jcr.Session;
34  import javax.jcr.observation.Event;
35  import javax.jcr.observation.EventIterator;
36  import javax.jcr.observation.EventListener;
37  import javax.jcr.observation.ObservationManager;
38  
39  import org.apache.commons.lang.StringUtils;
40  import org.codehaus.groovy.control.CompilationFailedException;
41  import org.exoplatform.container.ExoContainer;
42  import org.exoplatform.container.ExoContainerContext;
43  import org.exoplatform.container.component.ComponentPlugin;
44  import org.exoplatform.container.configuration.ConfigurationManager;
45  import org.exoplatform.container.xml.ObjectParameter;
46  import org.exoplatform.services.cache.CacheService;
47  import org.exoplatform.services.cms.BasePath;
48  import org.exoplatform.services.cms.impl.BaseResourceLoaderService;
49  import org.exoplatform.services.cms.impl.DMSConfiguration;
50  import org.exoplatform.services.cms.impl.DMSRepositoryConfiguration;
51  import org.exoplatform.services.cms.impl.ResourceConfig;
52  import org.exoplatform.services.cms.scripts.CmsScript;
53  import org.exoplatform.services.cms.scripts.ScriptService;
54  import org.exoplatform.services.jcr.RepositoryService;
55  import org.exoplatform.services.jcr.config.RepositoryEntry;
56  import org.exoplatform.services.jcr.core.ManageableRepository;
57  import org.exoplatform.services.jcr.ext.common.SessionProvider;
58  import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator;
59  import org.exoplatform.services.log.ExoLogger;
60  import org.exoplatform.services.log.Log;
61  import org.exoplatform.services.wcm.core.NodetypeConstant;
62  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
63  
64  public class ScriptServiceImpl extends BaseResourceLoaderService implements ScriptService, EventListener {
65  
66    private GroovyClassLoader groovyClassLoader_ ;
67    List<ScriptPlugin> plugins_ = new ArrayList<ScriptPlugin>() ;
68    private DMSConfiguration dmsConfiguration_;
69    private static final Log LOG  = ExoLogger.getLogger(ScriptServiceImpl.class.getName());
70    private Set<String> configuredScripts_;
71  
72    /**
73     * Constructor method
74     * Init repositoryService, configurationManager, nodeHierarchyCreator, caService, dmsConfiguration
75     * @param repositoryService       RepositoryService
76     * @param cservice                ConfigurationManager
77     * @param nodeHierarchyCreator    NodeHierarchyCreator
78     * @param cacheService            CacheService
79     * @param dmsConfiguration        DMSConfiguration
80     * @throws Exception
81     */
82    public ScriptServiceImpl(RepositoryService repositoryService, ConfigurationManager cservice,
83        NodeHierarchyCreator nodeHierarchyCreator, CacheService cacheService,
84        DMSConfiguration dmsConfiguration) throws Exception {
85      super(cservice, nodeHierarchyCreator, repositoryService, cacheService, dmsConfiguration);
86      groovyClassLoader_ = createGroovyClassLoader();
87      repositoryService_ = repositoryService ;
88      nodeHierarchyCreator_ = nodeHierarchyCreator ;
89      dmsConfiguration_ = dmsConfiguration;
90    }
91  
92    /**
93     * {@inheritDoc}
94     */
95    public void start() {
96      try {
97        initPlugins();
98      } catch (Exception e) {
99        if (LOG.isErrorEnabled()) {
100         LOG.error("Unexpected error", e);
101       }
102     }
103   }
104 
105   /**
106    * add ScriptPlugin
107    * @param plugin  ComponentPlugin
108    * @see           ScriptPlugin
109    * @see           ComponentPlugin
110    */
111   public void addScriptPlugin(ComponentPlugin plugin) {
112     if(plugin instanceof ScriptPlugin) {
113       plugins_.add((ScriptPlugin)plugin) ;
114     }
115   }
116 
117   /**
118    * init Plugin
119    * @see       Session
120    * @see       ScriptPlugin
121    * @see       RepositoryEntry
122    * @see       DMSRepositoryConfiguration
123    * @see       ObservationManager
124    * @throws Exception
125    */
126   private void initPlugins() throws Exception{
127     configuredScripts_ = new HashSet<String>();
128     Session session = null ;
129     String scriptsPath = getBasePath();
130     for(ScriptPlugin plugin : plugins_) {
131       String scriptsLocation = plugin.getPredefineScriptsLocation();
132       if(plugin.getAutoCreateInNewRepository()) {
133         DMSRepositoryConfiguration dmsRepoConfig = null;
134         dmsRepoConfig = dmsConfiguration_.getConfig();
135         session = repositoryService_.getCurrentRepository().getSystemSession(dmsRepoConfig.getSystemWorkspace());
136         Iterator<ObjectParameter> iter = plugin.getScriptIterator() ;
137         while(iter.hasNext()) {
138           ResourceConfig resourceConfig = (ResourceConfig) iter.next().getObject();
139           init(session,resourceConfig,scriptsLocation);
140           addConfigScripts(resourceConfig);
141         }
142         ObservationManager obsManager = session.getWorkspace().getObservationManager();
143         obsManager.addEventListener(this, Event.PROPERTY_CHANGED, scriptsPath, true, null, null, true);
144         session.save();
145         session.logout();
146       }
147       ManageableRepository mRepository = repositoryService_.getCurrentRepository();
148       DMSRepositoryConfiguration dmsDefaultRepoConfig = dmsConfiguration_.getConfig();
149       session = mRepository.getSystemSession(dmsDefaultRepoConfig.getSystemWorkspace()) ;
150       Iterator<ObjectParameter> iter = plugin.getScriptIterator() ;
151       while(iter.hasNext()) {
152         init(session,(ResourceConfig) iter.next().getObject(),scriptsLocation) ;
153       }
154       ObservationManager obsManager = session.getWorkspace().getObservationManager();
155       obsManager.addEventListener(this, Event.PROPERTY_CHANGED, scriptsPath, true, null, null, true);
156       session.save();
157       session.logout();
158     }
159   }
160 
161   /**
162    * get Base Script Path
163    * @see       NodeHierarchyCreator
164    * @return    String
165    */
166   protected String getBasePath() { return nodeHierarchyCreator_.getJcrPath(BasePath.CMS_SCRIPTS_PATH); }
167 
168   /**
169    * {@inheritDoc}
170    */
171   public void initRepo() throws Exception {
172     configuredScripts_ = new HashSet<String>();
173     ManageableRepository mRepository = repositoryService_.getCurrentRepository();
174     String scriptsPath = getBasePath();
175     DMSRepositoryConfiguration dmsRepoConfig = dmsConfiguration_.getConfig();
176     Session session = mRepository.getSystemSession(dmsRepoConfig.getSystemWorkspace()) ;
177     for(ScriptPlugin plugin : plugins_) {
178       if(!plugin.getAutoCreateInNewRepository()) continue ;
179       String scriptsLocation = plugin.getPredefineScriptsLocation();
180       Iterator<ObjectParameter> iter = plugin.getScriptIterator() ;
181       while(iter.hasNext()) {
182         ResourceConfig resourceConfig = (ResourceConfig) iter.next().getObject();
183         init(session, resourceConfig,scriptsLocation);
184         addConfigScripts(resourceConfig);
185       }
186       ObservationManager obsManager = session.getWorkspace().getObservationManager();
187       obsManager.addEventListener(this, Event.PROPERTY_CHANGED, scriptsPath, true, null, null, true);
188 
189     }
190     session.save();
191     session.logout();
192   }
193 
194   /**
195    * {@inheritDoc}
196    */
197   public Node getECMScriptHome(SessionProvider provider) throws Exception {
198     Session session = getSession(provider);
199     return getNodeByAlias(BasePath.ECM_EXPLORER_SCRIPTS,session);
200   }
201 
202   /**
203    * {@inheritDoc}
204    */
205   @Override
206   public List<Node> getECMActionScripts(SessionProvider provider) throws Exception {
207     Session session = getSession(provider);
208     return getScriptList(BasePath.ECM_ACTION_SCRIPTS, session);
209   }
210 
211   /**
212    * {@inheritDoc}
213    */
214   @Override
215   public List<Node> getECMInterceptorScripts(SessionProvider provider) throws Exception {
216     Session session = getSession(provider);
217     return getScriptList(BasePath.ECM_INTERCEPTOR_SCRIPTS, session);
218   }
219 
220   /**
221    * {@inheritDoc}
222    */
223   @Override
224   public List<Node> getECMWidgetScripts(SessionProvider provider) throws Exception {
225     Session session = getSession(provider);
226     return getScriptList(BasePath.ECM_WIDGET_SCRIPTS,session);
227   }
228 
229   /**
230    * {@inheritDoc}
231    */
232   @Override
233   public String getBaseScriptPath() throws Exception {
234     return getBasePath() ;
235   }
236 
237   /**
238    * {@inheritDoc}}
239    */
240   @Override
241   public String getScriptAsText(Node script) throws Exception {
242     return script.getNode(NodetypeConstant.JCR_CONTENT).getProperty(NodetypeConstant.JCR_DATA).getString();
243   }
244 
245   /**
246    * {@inheritDoc}
247    */
248   @Override
249   public synchronized CmsScript getScript(String scriptName) throws Exception {
250     CmsScript scriptObject = resourceCache_.get(scriptName);
251     if (scriptObject != null) return scriptObject;
252     ExoContainer container = ExoContainerContext.getCurrentContainer() ;
253     try {
254       scriptObject = (CmsScript) container.getComponentInstance(scriptName);
255       if(scriptObject !=null ) {
256         resourceCache_.put(scriptName, scriptObject) ;
257         return scriptObject;
258       }
259     } catch (NoClassDefFoundError e) {
260       if (LOG.isWarnEnabled()) {
261         LOG.warn(e.getMessage());
262       }
263     }
264 
265     groovyClassLoader_ = createGroovyClassLoader();
266     Class scriptClass = groovyClassLoader_.loadClass(scriptName) ;
267     container.registerComponentImplementation(scriptName, scriptClass);
268     scriptObject = (CmsScript) container.getComponentInstance(scriptName);
269     resourceCache_.put(scriptName, scriptObject) ;
270 
271     return scriptObject;
272   }
273 
274   /**
275    * {@inheritDoc}
276    */
277   @Override
278   public void addScript(String name, String text, SessionProvider provider) throws Exception {
279     addScript(name, name, text, provider);
280   }
281 
282   /**
283    * {@inheritDoc}
284    */
285   @Override
286   public void addScript(String name, String description, String text, SessionProvider provider) throws Exception {
287     Node resourcesHome = getResourcesHome(provider);
288     InputStream in = new ByteArrayInputStream(text.getBytes());
289     addResource(resourcesHome, name, description, in);
290     removeFromCache(name) ;
291   }
292 
293   /**
294    * {@inheritDoc}
295    */
296   @Override
297   public void removeScript(String scriptName, SessionProvider provider) throws Exception {
298     removeResource(scriptName, provider);
299     removeFromCache(scriptName) ;
300   }
301 
302   /**
303    * Get ScriptHome
304    * @param scriptAlias     String
305    *                        The alias of script
306    * @param session         Session
307    * @see                   NodeHierarchyCreator
308    * @return
309    * @throws Exception
310    */
311   private Node getScriptHome(String scriptAlias, Session session) throws Exception {
312     String path = nodeHierarchyCreator_.getJcrPath(scriptAlias) ;
313     return (Node)session.getItem(path);
314   }
315 
316   /**
317    * get Script List with the following param
318    * @param scriptAlias   String
319    *                      The alias of script
320    * @param session       Session
321    * @see                   NodeHierarchyCreator
322    * @return
323    * @throws Exception
324    */
325   private List<Node> getScriptList(String scriptAlias,Session session) throws Exception {
326     List<Node> scriptList = new ArrayList<Node>() ;
327     Node scriptHome = getScriptHome(scriptAlias,session) ;
328     for(NodeIterator iter = scriptHome.getNodes(); iter.hasNext() ;) {
329       scriptList.add(iter.nextNode()) ;
330     }
331     return scriptList;
332   }
333 
334   /**
335    * remove From Cache
336    * @param scriptName    String
337    *                      The name of script
338    * @see                 ExoContainer
339    * @see                 ExoContainerContext
340    */
341   @Override
342   protected void removeFromCache(String scriptName){
343     try{
344       Object cachedobject = resourceCache_.get(scriptName);
345       if (cachedobject != null) {
346         resourceCache_.remove(scriptName) ;
347         ExoContainer container = ExoContainerContext.getCurrentContainer();
348         container.unregisterComponent(scriptName);
349       }
350     } catch (Exception e) {
351       if (LOG.isWarnEnabled()) {
352         LOG.warn(e.getMessage());
353       }
354     }
355   }
356 
357   /**
358    * onEvent
359    * @param events      EventIterator
360    * @see               Session
361    * @see               DMSRepositoryConfiguration
362    * @see               ManageableRepository
363    */
364   @Override
365   public void onEvent(EventIterator events) {
366     while (events.hasNext()) {
367       Event event = events.nextEvent();
368       String path = null;
369       Session jcrSession = null ;
370       try {
371         path = event.getPath();
372         DMSRepositoryConfiguration dmsRepoConfig = null;
373         try {
374           ManageableRepository manageableRepository = repositoryService_.getCurrentRepository();
375           dmsRepoConfig = dmsConfiguration_.getConfig();
376           jcrSession = manageableRepository.getSystemSession(dmsRepoConfig.getSystemWorkspace());
377           Property property = (Property) jcrSession.getItem(path);
378           if ("jcr:data".equals(property.getName())) {
379             Node node = property.getParent().getParent();
380             removeFromCache(StringUtils.removeStart(StringUtils.removeStart(node.getPath(), this.getBaseScriptPath()), "/"));
381           }
382           jcrSession.logout();
383         } catch (Exception e) {
384           jcrSession.logout();
385           continue ;
386         }
387       } catch (Exception e) {
388         if (LOG.isErrorEnabled()) {
389           LOG.error("Unexpected error", e);
390         }
391       }
392     }
393   }
394 
395   /**
396    * create Groovy ClassLoader
397    * @see   SessionProvider
398    * @return
399    */
400   private GroovyClassLoader createGroovyClassLoader() {
401     ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
402     return new GroovyClassLoader(parentLoader) {
403       @SuppressWarnings("unchecked")
404       protected Class findClass(String className) throws ClassNotFoundException {
405         String filename = null ;
406         String nodeName = null ;
407         if(className.indexOf(":") > -1) {
408           String[] array = className.split(":") ;
409           nodeName = array[1] ;
410           filename = array[1].replace('.', File.separatorChar) + ".groovy";
411         }else {
412           nodeName = className ;
413           filename = className.replace('.', File.separatorChar) + ".groovy";
414         }
415         String scriptContent = null;
416         try {
417           scriptContent = WCMCoreUtils.getService(BaseResourceLoaderService.class).getResourceAsText(nodeName);
418         } catch (Exception e) {
419           throw new ClassNotFoundException("Could not read " + nodeName + ": " + e);
420         }
421         try {
422           return parseClass(scriptContent, filename);
423         } catch (CompilationFailedException e2) {
424           throw new ClassNotFoundException("Syntax error in " + filename
425               + ": " + e2);
426         }
427       }
428     };
429   }
430 
431   /**
432    * {@inheritDoc}
433    */
434   @Override
435   public Node getScriptNode(String scriptName,SessionProvider provider) throws Exception {
436     try {
437       Node scriptHome = getResourcesHome(provider) ;
438       return scriptHome.getNode(scriptName) ;
439     }catch (Exception e) {
440       return null;
441     }
442   }
443 
444 
445   /**
446    * Return session of the current repository
447    * @param provider        SessionProvider
448    * @return
449    * @see                   SessionProvider
450    * @see                   ManageableRepository
451    * @see                   DMSRepositoryConfiguration
452    * @throws Exception
453    */
454   private Session getSession(SessionProvider provider) throws Exception {
455     ManageableRepository manageableRepository = repositoryService_.getCurrentRepository();
456     DMSRepositoryConfiguration dmsRepoConfig = dmsConfiguration_.getConfig();
457     return provider.getSession(dmsRepoConfig.getSystemWorkspace(), manageableRepository);
458   }
459 
460   /**
461    * Get Node By Alias
462    * @param alias       String
463    *                    The alias of the specefied node
464    * @param session     Session
465    * @see               NodeHierarchyCreator
466    * @see               Session
467    * @return
468    * @throws Exception
469    */
470   private Node getNodeByAlias(String alias,Session session) throws Exception {
471     String path = nodeHierarchyCreator_.getJcrPath(alias) ;
472     return (Node)session.getItem(path);
473   }
474 
475   /**
476    * {@inheritDoc}}
477    */
478   @Override
479   public Set<String> getAllConfiguredScripts() {
480     return configuredScripts_;
481   }
482 
483   private void addConfigScripts(ResourceConfig resourceConfig) {
484     for (Object obj :  resourceConfig.getRessources()) {
485       if (obj instanceof ResourceConfig.Resource) {
486         ResourceConfig.Resource resource = (ResourceConfig.Resource)obj;
487         String name = resource.getName();
488         if (name.indexOf("/") >=0 ) {
489           name = name.substring(name.lastIndexOf("/") + 1);
490         }
491         configuredScripts_.add(name);
492       }
493     }
494   }
495 
496 }