/* * Copyright (C) 2003-2007 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. */ package org.exoplatform.services.cms.scripts.impl; import groovy.lang.GroovyClassLoader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Property; import javax.jcr.Session; import javax.jcr.observation.Event; import javax.jcr.observation.EventIterator; import javax.jcr.observation.EventListener; import javax.jcr.observation.ObservationManager; import org.apache.commons.lang.StringUtils; import org.codehaus.groovy.control.CompilationFailedException; import org.exoplatform.container.ExoContainer; import org.exoplatform.container.ExoContainerContext; import org.exoplatform.container.component.ComponentPlugin; import org.exoplatform.container.configuration.ConfigurationManager; import org.exoplatform.container.xml.ObjectParameter; import org.exoplatform.services.cache.CacheService; import org.exoplatform.services.cms.BasePath; import org.exoplatform.services.cms.impl.BaseResourceLoaderService; import org.exoplatform.services.cms.impl.DMSConfiguration; import org.exoplatform.services.cms.impl.DMSRepositoryConfiguration; import org.exoplatform.services.cms.impl.ResourceConfig; import org.exoplatform.services.cms.scripts.CmsScript; import org.exoplatform.services.cms.scripts.ScriptService; import org.exoplatform.services.jcr.RepositoryService; import org.exoplatform.services.jcr.config.RepositoryEntry; import org.exoplatform.services.jcr.core.ManageableRepository; import org.exoplatform.services.jcr.ext.common.SessionProvider; import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.wcm.core.NodetypeConstant; import org.exoplatform.services.wcm.utils.WCMCoreUtils; public class ScriptServiceImpl extends BaseResourceLoaderService implements ScriptService, EventListener { private GroovyClassLoader groovyClassLoader_ ; List<ScriptPlugin> plugins_ = new ArrayList<ScriptPlugin>() ; private DMSConfiguration dmsConfiguration_; private static final Log LOG = ExoLogger.getLogger(ScriptServiceImpl.class.getName()); private Set<String> configuredScripts_; /** * Constructor method * Init repositoryService, configurationManager, nodeHierarchyCreator, caService, dmsConfiguration * @param repositoryService RepositoryService * @param cservice ConfigurationManager * @param nodeHierarchyCreator NodeHierarchyCreator * @param cacheService CacheService * @param dmsConfiguration DMSConfiguration * @throws Exception */ public ScriptServiceImpl(RepositoryService repositoryService, ConfigurationManager cservice, NodeHierarchyCreator nodeHierarchyCreator, CacheService cacheService, DMSConfiguration dmsConfiguration) throws Exception { super(cservice, nodeHierarchyCreator, repositoryService, cacheService, dmsConfiguration); groovyClassLoader_ = createGroovyClassLoader(); repositoryService_ = repositoryService ; nodeHierarchyCreator_ = nodeHierarchyCreator ; dmsConfiguration_ = dmsConfiguration; } /** * {@inheritDoc} */ public void start() { try { initPlugins(); } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Unexpected error", e); } } } /** * add ScriptPlugin * @param plugin ComponentPlugin * @see ScriptPlugin * @see ComponentPlugin */ public void addScriptPlugin(ComponentPlugin plugin) { if(plugin instanceof ScriptPlugin) { plugins_.add((ScriptPlugin)plugin) ; } } /** * init Plugin * @see Session * @see ScriptPlugin * @see RepositoryEntry * @see DMSRepositoryConfiguration * @see ObservationManager * @throws Exception */ private void initPlugins() throws Exception{ configuredScripts_ = new HashSet<String>(); Session session = null ; String scriptsPath = getBasePath(); for(ScriptPlugin plugin : plugins_) { String scriptsLocation = plugin.getPredefineScriptsLocation(); if(plugin.getAutoCreateInNewRepository()) { DMSRepositoryConfiguration dmsRepoConfig = null; dmsRepoConfig = dmsConfiguration_.getConfig(); session = repositoryService_.getCurrentRepository().getSystemSession(dmsRepoConfig.getSystemWorkspace()); Iterator<ObjectParameter> iter = plugin.getScriptIterator() ; while(iter.hasNext()) { ResourceConfig resourceConfig = (ResourceConfig) iter.next().getObject(); init(session,resourceConfig,scriptsLocation); addConfigScripts(resourceConfig); } ObservationManager obsManager = session.getWorkspace().getObservationManager(); obsManager.addEventListener(this, Event.PROPERTY_CHANGED, scriptsPath, true, null, null, true); session.save(); session.logout(); } ManageableRepository mRepository = repositoryService_.getCurrentRepository(); DMSRepositoryConfiguration dmsDefaultRepoConfig = dmsConfiguration_.getConfig(); session = mRepository.getSystemSession(dmsDefaultRepoConfig.getSystemWorkspace()) ; Iterator<ObjectParameter> iter = plugin.getScriptIterator() ; while(iter.hasNext()) { init(session,(ResourceConfig) iter.next().getObject(),scriptsLocation) ; } ObservationManager obsManager = session.getWorkspace().getObservationManager(); obsManager.addEventListener(this, Event.PROPERTY_CHANGED, scriptsPath, true, null, null, true); session.save(); session.logout(); } } /** * get Base Script Path * @see NodeHierarchyCreator * @return String */ protected String getBasePath() { return nodeHierarchyCreator_.getJcrPath(BasePath.CMS_SCRIPTS_PATH); } /** * {@inheritDoc} */ public void initRepo() throws Exception { configuredScripts_ = new HashSet<String>(); ManageableRepository mRepository = repositoryService_.getCurrentRepository(); String scriptsPath = getBasePath(); DMSRepositoryConfiguration dmsRepoConfig = dmsConfiguration_.getConfig(); Session session = mRepository.getSystemSession(dmsRepoConfig.getSystemWorkspace()) ; for(ScriptPlugin plugin : plugins_) { if(!plugin.getAutoCreateInNewRepository()) continue ; String scriptsLocation = plugin.getPredefineScriptsLocation(); Iterator<ObjectParameter> iter = plugin.getScriptIterator() ; while(iter.hasNext()) { ResourceConfig resourceConfig = (ResourceConfig) iter.next().getObject(); init(session, resourceConfig,scriptsLocation); addConfigScripts(resourceConfig); } ObservationManager obsManager = session.getWorkspace().getObservationManager(); obsManager.addEventListener(this, Event.PROPERTY_CHANGED, scriptsPath, true, null, null, true); } session.save(); session.logout(); } /** * {@inheritDoc} */ public Node getECMScriptHome(SessionProvider provider) throws Exception { Session session = getSession(provider); return getNodeByAlias(BasePath.ECM_EXPLORER_SCRIPTS,session); } /** * {@inheritDoc} */ @Override public List<Node> getECMActionScripts(SessionProvider provider) throws Exception { Session session = getSession(provider); return getScriptList(BasePath.ECM_ACTION_SCRIPTS, session); } /** * {@inheritDoc} */ @Override public List<Node> getECMInterceptorScripts(SessionProvider provider) throws Exception { Session session = getSession(provider); return getScriptList(BasePath.ECM_INTERCEPTOR_SCRIPTS, session); } /** * {@inheritDoc} */ @Override public List<Node> getECMWidgetScripts(SessionProvider provider) throws Exception { Session session = getSession(provider); return getScriptList(BasePath.ECM_WIDGET_SCRIPTS,session); } /** * {@inheritDoc} */ @Override public String getBaseScriptPath() throws Exception { return getBasePath() ; } /** * {@inheritDoc}} */ @Override public String getScriptAsText(Node script) throws Exception { return script.getNode(NodetypeConstant.JCR_CONTENT).getProperty(NodetypeConstant.JCR_DATA).getString(); } /** * {@inheritDoc} */ @Override public synchronized CmsScript getScript(String scriptName) throws Exception { CmsScript scriptObject = resourceCache_.get(scriptName); if (scriptObject != null) return scriptObject; ExoContainer container = ExoContainerContext.getCurrentContainer() ; try { scriptObject = (CmsScript) container.getComponentInstance(scriptName); if(scriptObject !=null ) { resourceCache_.put(scriptName, scriptObject) ; return scriptObject; } } catch (NoClassDefFoundError e) { if (LOG.isWarnEnabled()) { LOG.warn(e.getMessage()); } } groovyClassLoader_ = createGroovyClassLoader(); Class scriptClass = groovyClassLoader_.loadClass(scriptName) ; container.registerComponentImplementation(scriptName, scriptClass); scriptObject = (CmsScript) container.getComponentInstance(scriptName); resourceCache_.put(scriptName, scriptObject) ; return scriptObject; } /** * {@inheritDoc} */ @Override public void addScript(String name, String text, SessionProvider provider) throws Exception { addScript(name, name, text, provider); } /** * {@inheritDoc} */ @Override public void addScript(String name, String description, String text, SessionProvider provider) throws Exception { Node resourcesHome = getResourcesHome(provider); InputStream in = new ByteArrayInputStream(text.getBytes()); addResource(resourcesHome, name, description, in); removeFromCache(name) ; } /** * {@inheritDoc} */ @Override public void removeScript(String scriptName, SessionProvider provider) throws Exception { removeResource(scriptName, provider); removeFromCache(scriptName) ; } /** * Get ScriptHome * @param scriptAlias String * The alias of script * @param session Session * @see NodeHierarchyCreator * @return * @throws Exception */ private Node getScriptHome(String scriptAlias, Session session) throws Exception { String path = nodeHierarchyCreator_.getJcrPath(scriptAlias) ; return (Node)session.getItem(path); } /** * get Script List with the following param * @param scriptAlias String * The alias of script * @param session Session * @see NodeHierarchyCreator * @return * @throws Exception */ private List<Node> getScriptList(String scriptAlias,Session session) throws Exception { List<Node> scriptList = new ArrayList<Node>() ; Node scriptHome = getScriptHome(scriptAlias,session) ; for(NodeIterator iter = scriptHome.getNodes(); iter.hasNext() ;) { scriptList.add(iter.nextNode()) ; } return scriptList; } /** * remove From Cache * @param scriptName String * The name of script * @see ExoContainer * @see ExoContainerContext */ @Override protected void removeFromCache(String scriptName){ try{ Object cachedobject = resourceCache_.get(scriptName); if (cachedobject != null) { resourceCache_.remove(scriptName) ; ExoContainer container = ExoContainerContext.getCurrentContainer(); container.unregisterComponent(scriptName); } } catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn(e.getMessage()); } } } /** * onEvent * @param events EventIterator * @see Session * @see DMSRepositoryConfiguration * @see ManageableRepository */ @Override public void onEvent(EventIterator events) { while (events.hasNext()) { Event event = events.nextEvent(); String path = null; Session jcrSession = null ; try { path = event.getPath(); DMSRepositoryConfiguration dmsRepoConfig = null; try { ManageableRepository manageableRepository = repositoryService_.getCurrentRepository(); dmsRepoConfig = dmsConfiguration_.getConfig(); jcrSession = manageableRepository.getSystemSession(dmsRepoConfig.getSystemWorkspace()); Property property = (Property) jcrSession.getItem(path); if ("jcr:data".equals(property.getName())) { Node node = property.getParent().getParent(); removeFromCache(StringUtils.removeStart(StringUtils.removeStart(node.getPath(), this.getBaseScriptPath()), "/")); } jcrSession.logout(); } catch (Exception e) { jcrSession.logout(); continue ; } } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Unexpected error", e); } } } } /** * create Groovy ClassLoader * @see SessionProvider * @return */ private GroovyClassLoader createGroovyClassLoader() { ClassLoader parentLoader = Thread.currentThread().getContextClassLoader(); return new GroovyClassLoader(parentLoader) { @SuppressWarnings("unchecked") protected Class findClass(String className) throws ClassNotFoundException { String filename = null ; String nodeName = null ; if(className.indexOf(":") > -1) { String[] array = className.split(":") ; nodeName = array[1] ; filename = array[1].replace('.', File.separatorChar) + ".groovy"; }else { nodeName = className ; filename = className.replace('.', File.separatorChar) + ".groovy"; } String scriptContent = null; try { scriptContent = WCMCoreUtils.getService(BaseResourceLoaderService.class).getResourceAsText(nodeName); } catch (Exception e) { throw new ClassNotFoundException("Could not read " + nodeName + ": " + e); } try { return parseClass(scriptContent, filename); } catch (CompilationFailedException e2) { throw new ClassNotFoundException("Syntax error in " + filename + ": " + e2); } } }; } /** * {@inheritDoc} */ @Override public Node getScriptNode(String scriptName,SessionProvider provider) throws Exception { try { Node scriptHome = getResourcesHome(provider) ; return scriptHome.getNode(scriptName) ; }catch (Exception e) { return null; } } /** * Return session of the current repository * @param provider SessionProvider * @return * @see SessionProvider * @see ManageableRepository * @see DMSRepositoryConfiguration * @throws Exception */ private Session getSession(SessionProvider provider) throws Exception { ManageableRepository manageableRepository = repositoryService_.getCurrentRepository(); DMSRepositoryConfiguration dmsRepoConfig = dmsConfiguration_.getConfig(); return provider.getSession(dmsRepoConfig.getSystemWorkspace(), manageableRepository); } /** * Get Node By Alias * @param alias String * The alias of the specefied node * @param session Session * @see NodeHierarchyCreator * @see Session * @return * @throws Exception */ private Node getNodeByAlias(String alias,Session session) throws Exception { String path = nodeHierarchyCreator_.getJcrPath(alias) ; return (Node)session.getItem(path); } /** * {@inheritDoc}} */ @Override public Set<String> getAllConfiguredScripts() { return configuredScripts_; } private void addConfigScripts(ResourceConfig resourceConfig) { for (Object obj : resourceConfig.getRessources()) { if (obj instanceof ResourceConfig.Resource) { ResourceConfig.Resource resource = (ResourceConfig.Resource)obj; String name = resource.getName(); if (name.indexOf("/") >=0 ) { name = name.substring(name.lastIndexOf("/") + 1); } configuredScripts_.add(name); } } } }