/* * Created on Nov 19, 2004 */ package com.openedit; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Element; import org.openedit.xml.XmlArchive; import org.openedit.xml.XmlFile; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import com.openedit.modules.BaseModule; import com.openedit.page.Page; import com.openedit.page.PageAction; import com.openedit.page.PageRequestKeys; import com.openedit.page.PageSettings; import com.openedit.page.manage.PageManager; import com.openedit.util.PathUtilities; /** * @author Matthew Avery, mavery@einnovation.com */ public class ModuleManager implements BeanFactoryAware, ShutdownList { protected ConfigurableListableBeanFactory fieldBeanFactory; protected Set fieldLoadedBeans; protected XmlArchive fieldXmlArchive; protected Map fieldCatalogIdBeans; protected BeanNameLoader fieldBeanLoader; public BeanNameLoader getBeanNameLoader() { return fieldBeanLoader; } public void setBeanNameLoader(BeanNameLoader inBeanLoader) { fieldBeanLoader = inBeanLoader; } private static final Log log = LogFactory.getLog(ModuleManager.class); public void executePageAction( PageAction inAction, WebPageRequest inReq ) throws OpenEditException { //if( log.isDebugEnabled() ) { log.debug("Running " + inAction.getActionName() + " from " + inAction.getPath() + " " + inReq.getPath()); } Object module = getBean( inAction.getModuleName() ); if ( module == null ) { throw new OpenEditException("Error attempting to execute page action " + inAction.getActionName() + ". No module found: " + inAction.getModuleName()); } String methodName = inAction.getMethodName(); execMethod(module,methodName, inAction,inReq); } public Object execute( String inFullName, WebPageRequest inReq) throws OpenEditException { int dot = inFullName.indexOf("."); if (dot == -1) { throw new OpenEditException("No command found" + inFullName); } else { String mod = inFullName.substring(0, dot); String function = inFullName.substring(dot + 1); Object module = getBean(mod); return execMethod(module,function,inReq); } } protected Object execMethod(Object module, String methodName,WebPageRequest inReq ) throws OpenEditException { return execMethod(module,methodName,null,inReq); } protected Object execMethod(Object module, String methodName,PageAction inAction, WebPageRequest inReq ) throws OpenEditException { if (module instanceof Secured) { Secured secured = (Secured) module; if (!secured.canRun(inReq, methodName)) { throw new OpenEditException("User does not have permission to run method " + methodName + " on " + module.getClass()); } } Object returned = null; if ( inAction != null) { inReq.setCurrentAction(inAction); } try { //we only want to support webpagereq method for security reasons Method method = module.getClass().getMethod( methodName, new Class[]{ WebPageRequest.class } ); if ( method != null ) { returned = method.invoke( module, new Object[]{ inReq } ); } } catch ( InvocationTargetException ex) { Throwable cause = ex.getTargetException(); if ( cause instanceof OpenEditException) { throw (OpenEditException)cause; } else if ( inAction!=null) { throw new OpenEditException(cause,inAction.getPath() +"#" + inAction.getActionName()); } else { throw new OpenEditException(cause); } } catch (Exception e) { log.error(e); if ( inReq != null ) { log.error("When loading: " + inReq.getPath() + "#" + module.getClass().getName() + "." + methodName); } throw new OpenEditException(e); } return returned; } public boolean runActions(WebPageRequest inReq) { String runpath = inReq.findValue("runpath"); Page page = getPageManager().getPage(runpath); WebPageRequest child = inReq.copy(page); if(page == null) { return false; } executePathActions(page, child); if( child.hasCancelActions() && !child.hasRedirected()) { executePageActions(page, child); } return !child.hasCancelActions(); } public void executePageActions( Page inPage, WebPageRequest inPageRequest ) throws OpenEditException { List actions = inPage.getPageActions(); if (actions == null) { return; } for (Iterator iter = actions.iterator(); iter.hasNext();) { PageAction pageAction = (PageAction) iter.next(); if ( inPageRequest.hasCancelActions() || inPageRequest.hasRedirected() || inPageRequest.hasForwarded() ) { return; } executePageAction( pageAction, inPageRequest ); } } public void executePathActions( Page inPage, WebPageRequest inPageRequest ) throws OpenEditException { List actions = inPage.getPathActions(); //a list of action sorted from lower pages up to the root String method = inPageRequest.getMethod(); List copy = condenseActions(method, actions ); //reverse the list with the root run first for (Iterator iter = copy.iterator(); iter.hasNext();) { //TODO: Add mime type checks to speed this up PageAction pageAction = (PageAction) iter.next(); executePageAction( pageAction, inPageRequest ); if ( inPageRequest.hasCancelActions() || inPageRequest.hasRedirected() || inPageRequest.hasForwarded() ) { return; } } //This includes request actions String[] actionNames = inPageRequest.getRequestActions(); if ( actionNames != null && actionNames.length > 0) { //check permissions boolean ok = false; String reqactions = (String)inPageRequest.getContentPage().get(PageRequestKeys.ALLOWPATHREQUESTACTIONS); if ( reqactions != null) { ok = reqactions.equalsIgnoreCase("true"); } else if ( inPageRequest.getUser() != null ) { ok = true; } if ( ok ) { for (int i = 0; i < actionNames.length; i++) { if ( actionNames[i].length() > 0 ) { executePageAction(new PageAction( actionNames[i] ), inPageRequest ); if ( inPageRequest.hasCancelActions() || inPageRequest.hasRedirected() ) { return; } } } } } } public List condenseActions(String method, List inActions) { //remove any duplicates keeping the ones on the end first if( inActions.size() < 2) { return inActions; } List copy = new ArrayList(inActions.size() ); Set copynames = new HashSet(inActions.size()); Set cancellist = new HashSet( 2 ); for (int i = inActions.size()-1; i >= 0; i--) //start at the end from /sub/sdfds.xconf first { PageAction pageAction = (PageAction) inActions.get(i); String targetmethod = pageAction.getConfig().getAttribute("method"); if(targetmethod != null && !targetmethod.equals(method)){ continue; } String cancel = pageAction.getConfig().getAttribute("cancel"); if ( Boolean.parseBoolean(cancel) ) { cancellist.add(pageAction.getActionName()); } if( cancellist.contains(pageAction.getActionName())) { continue; } String allow = pageAction.getConfig().getAttribute("allowduplicates"); if( Boolean.parseBoolean(allow)) { copy.add(pageAction); //This is so the child action is added first then the parent is excluded unless it has allowduplicated turned on //copynames.add(pageAction.getActionName()); } else { if( !copynames.contains(pageAction.getActionName())) { copy.add(pageAction); copynames.add(pageAction.getActionName()); } } } Collections.reverse(copy); return copy; } public boolean contains(String inCatalogId, String inBeanName) { String beanName = resolveBean(inCatalogId, inBeanName); if( getCatalogIdBeans().containsKey(inCatalogId + "_" + inBeanName) ) { return true; } return contains(beanName); } public Object getBean( String inCatalogId, String inBeanName ) { return getBean(inCatalogId,inBeanName,true); } public Object getBean( String inCatalogId, String inBeanName, boolean inCached ) { if( !inCached ) { return loadBean(inCatalogId, inBeanName); } Object bean = getCatalogIdBeans().get(inCatalogId + "_" + inBeanName); if( bean == null) { synchronized (this) { bean = getCatalogIdBeans().get(inCatalogId + "_" + inBeanName); if( bean != null) { return bean; } bean = loadBean(inCatalogId, inBeanName); //if instanceof GroovyBean //bean = bean.getProxy() getCatalogIdBeans().put(inCatalogId + "_" + inBeanName, bean); } } return bean; } private Object loadBean(String inCatalogId, String inBeanName) { Object bean; String beanName = resolveBean(inCatalogId, inBeanName); bean = getBean(beanName); try { Method catalogSetter = bean.getClass().getMethod("setCatalogId", new Class[] {String.class}); catalogSetter.invoke(bean, new Object[] {inCatalogId}); } catch (Exception e) { //Could not set catalogId } return bean; } public String resolveBean(String inCatalogId, String inBeanName) { return getBeanNameLoader().findName(inCatalogId, inBeanName); } protected PageManager getPageManager() { return (PageManager)getBean("pageManager"); } public Object getBean( String inBeanName ) { try { Object bean = getBeanFactory().getBean( inBeanName ); if( bean != null && bean instanceof Shutdownable) { addForShutdown((Shutdownable)bean); } return bean; } catch ( NoSuchBeanDefinitionException ex) { throw new OpenEditRuntimeException("Could not find bean named " + inBeanName, ex); } } /** * returns "null" if the module does not exist. * * @author Matthew Avery, mavery@einnovation.com */ public BaseModule getModule( String inName ) { Object bean = getBean( inName ); if ( bean != null && bean instanceof BaseModule ) { return (BaseModule) bean; } return null; } protected BeanFactory getBeanFactory() { return fieldBeanFactory; } public void setBeanFactory(BeanFactory inBeanFactory) { //I assume this is true most of the times since OpenEdit is loading up this ModuleManager fieldBeanFactory = (ConfigurableListableBeanFactory)inBeanFactory; } /** * @param inKey * @return */ public boolean contains(String inKey) { boolean has = getBeanFactory().containsBean(inKey); //log.info("HadBean " + inKey + " = " + has); return has; } public Set<Shutdownable> getLoadedBeans() { if (fieldLoadedBeans == null) { fieldLoadedBeans = new HashSet(); } return fieldLoadedBeans; } public void addForShutdown(Shutdownable inAble) { getLoadedBeans().add(inAble); } // public List listAllBeans() // { // List allModules = new ArrayList(); // List sortedNames = new ArrayList(); // // String[] names = getBeanFactory().getBeanDefinitionNames(); // sortedNames = Arrays.asList(names); // // for (int i = 0; i < sortedNames.size(); i++) { // String name = (String) sortedNames.get(i); // Bean bean = createBean(name); // if (bean != null) { // allModules.add(bean); // } // } // return allModules; // } // public String getVersion(String inBeanName) // { // if (getBeanFactory().containsBean(inBeanName)) { // BeanDefinition beanDe = getBeanFactory().getBeanDefinition(inBeanName); // if (!(beanDe instanceof AbstractBeanDefinition)) { // throw new OpenEditRuntimeException("Spring version not supported yet"); // // } // AbstractBeanDefinition beanDef = (AbstractBeanDefinition) beanDe; // // try { // beanDef.resolveBeanClass(Thread.currentThread().getContextClassLoader()); // } catch (Exception ex) { // log.info("Could not load: " + inBeanName + " " + ex); // return null; // } // // // get version of module // String version = beanDef.getBeanClass().getPackage().getImplementationVersion(); // return version; // // get title of module //// String title = beanDef.getBeanClass().getPackage().getImplementationTitle(); //// bean.setTitle(title); // //return bean; // } // return null; // } public XmlArchive getXmlArchive() { if( fieldXmlArchive == null) { fieldXmlArchive = (XmlArchive)getBean("xmlArchive"); } return fieldXmlArchive; } public void setXmlArchive(XmlArchive inXmlArchive) { fieldXmlArchive = inXmlArchive; } public Map getCatalogIdBeans() { if (fieldCatalogIdBeans == null) { fieldCatalogIdBeans = new HashMap(); } return fieldCatalogIdBeans; } public void setCatalogIdBeans(Map inCatalogIdBeans) { fieldCatalogIdBeans = inCatalogIdBeans; } public void clearBean( String inCatalogId, String inBeanName ) { Object obj = getCatalogIdBeans().get(inCatalogId + "_" + inBeanName); if( obj instanceof Shutdownable) { ((Shutdownable)obj).shutdown(); } getCatalogIdBeans().remove(inCatalogId + "_" + inBeanName); } }