diff --git a/ccm-core/src/com/arsdigita/web/BaseApplicationServlet.java b/ccm-core/src/com/arsdigita/web/BaseApplicationServlet.java index 44de6bfbe..b8b8dcc0f 100755 --- a/ccm-core/src/com/arsdigita/web/BaseApplicationServlet.java +++ b/ccm-core/src/com/arsdigita/web/BaseApplicationServlet.java @@ -65,7 +65,10 @@ import org.apache.log4j.Logger; */ public abstract class BaseApplicationServlet extends BaseServlet { - /** Logger instance for debugging purpose. */ + /** Internal logger instance to faciliate debugging. Enable logging output + * by editing /WEB-INF/conf/log4j.properties int hte runtime environment + * and set com.arsdigita.web.BaseApplicationServlet=DEBUG by uncommenting + * or adding the line. */ private static final Logger s_log = Logger.getLogger(BaseApplicationServlet.class); /** diff --git a/ccm-core/src/com/arsdigita/web/CCMApplicationContextListener.java b/ccm-core/src/com/arsdigita/web/CCMApplicationContextListener.java index 5558a66de..818edb241 100644 --- a/ccm-core/src/com/arsdigita/web/CCMApplicationContextListener.java +++ b/ccm-core/src/com/arsdigita/web/CCMApplicationContextListener.java @@ -34,13 +34,14 @@ import org.apache.log4j.LogManager; import org.apache.log4j.PropertyConfigurator; /** - * Web application lifecycle listener, used to perform central initialisation tasks at CCM startup - * in a Servlet container / web application server, expecially setting the runtime context (file - * locations) and (in the future) the database connection. + * Web application lifecycle listener, used to perform central initialisation + * tasks at CCM startup in a Servlet container / web application server, + * expecially setting the runtime context (file locations) and (in the future) + * the database connection. * - * The methods of this classes are by definition only invoked by the Servlet container / web - * application server, not by any Servlet or java class of the application itself! Invocation is - * managed by the deployment descriptor. + * The methods of this classes are by definition only invoked by the Servlet + * container / web application server, not by any Servlet or java class of the + * application itself! Invocation is managed by the deployment descriptor. * * Note! Don't forget to configure it in web.xml deployment descriptor! * @@ -48,25 +49,31 @@ import org.apache.log4j.PropertyConfigurator; * com.arsdigita.runtime.CCMApplicationContextListener * * - * According to the 2.3 specification these tags must be placed after the filter tags and before the - * Servlet tags! + * According to the 2.3 specification these tags must be placed after the + * filter tags and before the Servlet tags! * * @author pboy * @version $Id: $ */ public class CCMApplicationContextListener implements ServletContextListener { - private static Logger s_log = Logger.getLogger(CCMApplicationContextListener.class); + /** Internal logger instance to faciliate debugging. Enable logging output + * by editing /WEB-INF/conf/log4j.properties int hte runtime environment + * and set com.arsdigita.web.CCMApplicationContextListener=DEBUG by + * uncommenting or adding the line. */ + private static final Logger s_log = Logger.getLogger( + CCMApplicationContextListener.class); private static Runtime runtime; /** - * Used to initialise classes at startup of the application, most of which needs to be plain - * java objects (because they are also used by command line interface - installation, - * configuration, maintenance). + * Used to initialise classes at startup of the application, most of which + * needs to be plain java objects (because they are also used by command + * line interface - installation, configuration, maintenance). * - * Here we provide one of the two supported ways to bring up the CCM application. This handles - * the startup inside a Servlet container. The command line utilities handle the startup there. + * Here we provide one of the two supported ways to bring up the CCM + * application. This handles the startup inside a Servlet container. + * The command line utilities handle the startup there. * Both initialise the same set of classes needed for CCM operations * * @param applicationStartEvent @@ -124,6 +131,7 @@ public class CCMApplicationContextListener implements ServletContextListener { * * @param applicationEndEvent */ + @Override public void contextDestroyed(ServletContextEvent applicationEndEvent) { s_log.info("Shutdown procedure started."); diff --git a/ccm-core/src/com/arsdigita/web/Web.java b/ccm-core/src/com/arsdigita/web/Web.java index 023f59a18..3f24d3d92 100755 --- a/ccm-core/src/com/arsdigita/web/Web.java +++ b/ccm-core/src/com/arsdigita/web/Web.java @@ -115,8 +115,7 @@ public class Web { /** * Gets the servlet request object of the current thread. * - * @return The current HttpServletRequest; it can be - * null + * @return The current HttpServletRequest; it can be null */ public static HttpServletRequest getRequest() { return (HttpServletRequest) s_request.get(); @@ -126,7 +125,6 @@ public class Web { * Gets the servlet context of the current thread. * * @return The current ServletContext; it can be null - * */ public static ServletContext getServletContext() { return (ServletContext) s_servletContext.get(); diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorConfig.java b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorConfig.java index 3b973899d..05f7c9d5b 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorConfig.java +++ b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorConfig.java @@ -65,7 +65,8 @@ public class ThemeDirectorConfig extends AbstractConfig { return s_conf; } - // set of configuration parameters + // ///////////////////////////////////////////////////////////////// + // Set of Configuration Parameters // ///////////////////////////////////////////////////////////////// /** Directory that all of the default themes are copied from. */ @@ -74,19 +75,19 @@ public class ThemeDirectorConfig extends AbstractConfig { ("themedirector.default_theme_path", Parameter.OPTIONAL, "/themes/master/"); - /** Servlet context path containing the default theme. - * Previously ccm-themedirector used to be installed in its own - * web context. In this case the appropriate web context should - * be specified. - * Currently, it is installed as part of the main application, - * therefore it is empty by default. - * @deprecated without direct replacement. Themedirector's Webapp context - * has to be determined at runtime. - */ - private final Parameter m_defaultThemeContext = - new StringParameter - ("themedirector.default_theme_context", - Parameter.OPTIONAL, ""); +// /** Servlet context path containing the default theme. +// * Previously ccm-themedirector used to be installed in its own +// * web context. In this case the appropriate web context should +// * be specified. +// * Currently, it is installed as part of the main application, +// * therefore it is empty by default. +// * @deprecated without direct replacement. Themedirector's Webapp context +// * has to be determined at runtime. +// */ +// private final Parameter m_defaultThemeContext = +// new StringParameter +// ("themedirector.default_theme_context", +// Parameter.OPTIONAL, ""); // Parameter.OPTIONAL, "/ccm-themedirector/"); /** File containing the default themes directory. Used in conjuntion with @@ -143,10 +144,11 @@ public class ThemeDirectorConfig extends AbstractConfig { */ public ThemeDirectorConfig() { - register(m_fileExtParam); - register(m_defaultThemeContext); - register(m_defaultThemeManifest); register(m_defaultThemePath); +// register(m_defaultThemeContext); + register(m_defaultThemeManifest); + register(m_fileExtParam); + register(m_themeDevFileWatchStartupDelay); register(m_themeDevFileWatchPollDelay); register(m_themePubFileWatchStartupDelay); @@ -155,6 +157,88 @@ public class ThemeDirectorConfig extends AbstractConfig { loadInfo(); } + + // ///////////////////////////////////////////////////////////////// + // Set of Configuration Parameters + // ///////////////////////////////////////////////////////////////// + + + /** + * Retrieves the path to a directory containing a complete set of theme + * files to copy into a new theme as a default implementation. + * By default it is set to a master directory containing the distribution's + * default theme. When creating a new theme it is copied over to provide + * a default for the new theme. + * + * Developer's note: + * Previously it was used as a string to filter a files directory stored in + * the Manifest file. Matching files were copied to the new theme's directory. + * The reason to use this approach is not documented. As a guess: it enables + * to copy from more than one directory, if those directories share a common + * name part which is specified here. But because all files must be present + * at deploy time to be included into the Manifest file it makes no sense. + * From original comment: "Specifically, if this is not null + * (or the empty string) than any file that is used as part of + * the default directory must start with this string." + * + * @return name of a directory containing a default theme implementation + */ + public String getDefaultThemePath() { + String defaultThemePath = (String)get(m_defaultThemePath); + if (defaultThemePath == null || defaultThemePath.trim().length() == 0) { + return null; + } + // remove leading slashwhich is already included in themedirector's + // constants of the directory layout. + if (defaultThemePath.startsWith("/")) { + defaultThemePath = defaultThemePath.substring(1); + } + return defaultThemePath; + } + +// /** +// * This returns the name of the servlet context containing +// * the default theme. +// * +// * @return +// * @deprecated without direct replacement, See note above +// */ +// public String getDefaultThemeContext() { +// String ctx = (String)get(m_defaultThemeContext); +// if (ctx == null) { +// ctx = "/"; +// } +// if (!ctx.endsWith("/")) { +// ctx = ctx + "/"; +// } +// if (!ctx.startsWith("/")) { +// ctx = "/" + ctx; +// } +// return ctx; +// } + + /** + * This returns the name of the manifest file containing a list of default + * theme. + * + * @return + * @deprecated replaced by a direct copy from the default directory + */ + public String getDefaultThemeManifest() { + return (String)get(m_defaultThemeManifest); + } + + + private static final String DEFAULT_THEME_URL = + ThemeDirector.DEFAULT_THEME + "." + Theme.URL; + private static final String DEFAULT_THEME_URL_ATTRIBUTE = + "defaultThemeURLAttribute"; + + /** + * Purpose uncodumented. + * + * @return + */ public Collection getDownloadFileExtensions() { if (m_downloadFileExtensions == null) { String extensions = (String)get(m_fileExtParam); @@ -170,90 +254,8 @@ public class ThemeDirectorConfig extends AbstractConfig { } - /** - * The number of seconds to wait before checking the database - * for the first time. A value of 0 means that the thread - * should not be started. This checks for published files. - */ - public Integer getThemePubFileWatchStartupDelay() { - return (Integer)get(m_themePubFileWatchStartupDelay); - } - - /** - * Returns the number of seconds between checking for updated - * files in the file system. This checks for published files. - */ - public Integer getThemePubFileWatchPollDelay() { - return (Integer)get(m_themePubFileWatchPollDelay); - } - - /** - * The number of seconds to wait before checking the database - * for the first time. A value of 0 means that the thread - * should not be started. This checks for development files. - */ - public Integer getThemeDevFileWatchStartupDelay() { - return (Integer)get(m_themeDevFileWatchStartupDelay); - } - - /** - * Returns the number of seconds between checking for updated - * files in the file system. This checks for development files. - */ - public Integer getThemeDevFileWatchPollDelay() { - return (Integer)get(m_themeDevFileWatchPollDelay); - } - - /** - * This returns the name of the servlet context containing - * the default theme - */ - public String getDefaultThemeContext() { - String ctx = (String)get(m_defaultThemeContext); - if (ctx == null) { - ctx = "/"; - } - if (!ctx.endsWith("/")) { - ctx = ctx + "/"; - } - if (!ctx.startsWith("/")) { - ctx = "/" + ctx; - } - return ctx; - } - - /** - * This returns the name of the manifest file containing - * the default theme. - */ - public String getDefaultThemeManifest() { - return (String)get(m_defaultThemeManifest); - } - - /** - * This returns a string that can be used as a file filter for - * the default directory. Specifically, if this is not null - * (or the empty string) than any file that is used as part of - * the default directory must start with this string - */ - public String getDefaultThemePath() { - String defaultThemePath = (String)get(m_defaultThemePath); - if (defaultThemePath == null || defaultThemePath.trim().length() == 0) { - return null; - } - if (defaultThemePath.startsWith("/")) { - defaultThemePath = defaultThemePath.substring(1); - } - return defaultThemePath; - } - - - private static final String DEFAULT_THEME_URL = - ThemeDirector.DEFAULT_THEME + "." + Theme.URL; - private static final String DEFAULT_THEME_URL_ATTRIBUTE = - "defaultThemeURLAttribute"; - /** + * Purpose undocumented. * * @param req * @return @@ -285,4 +287,45 @@ public class ThemeDirectorConfig extends AbstractConfig { req.setAttribute( DEFAULT_THEME_URL_ATTRIBUTE, themeURL ); return themeURL; } + + + /** + * The number of seconds to wait before checking the database + * for the first time. A value of 0 means that the thread + * should not be started. This checks for published files. + * + * @return + */ + public Integer getThemePubFileWatchStartupDelay() { + return (Integer)get(m_themePubFileWatchStartupDelay); + } + + /** + * Returns the number of seconds between checking for updated + * files in the file system. This checks for published files. + * + * @return + */ + public Integer getThemePubFileWatchPollDelay() { + return (Integer)get(m_themePubFileWatchPollDelay); + } + + /** + * The number of seconds to wait before checking the database + * for the first time. A value of 0 means that the thread + * should not be started. This checks for development files. + * @return + */ + public Integer getThemeDevFileWatchStartupDelay() { + return (Integer)get(m_themeDevFileWatchStartupDelay); + } + + /** + * Returns the number of seconds between checking for updated + * files in the file system. This checks for development files. + * @return + */ + public Integer getThemeDevFileWatchPollDelay() { + return (Integer)get(m_themeDevFileWatchPollDelay); + } } diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorConstants.java b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorConstants.java index e8f0d97ff..37ac40cd6 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorConstants.java +++ b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorConstants.java @@ -35,27 +35,29 @@ public interface ThemeDirectorConstants { public final static String DEV_DIR_STUB = "/devel-themedir"; /** Path stub into directory for production themes (sub-dir of THEMES_DIR). - * According to JavaEE spec with leading but without trailing "/"! */ + * According to JavaEE spec with leading "/", but deviating from the + * JavaEE spec we add a trailing "/" for backwards compatibility to + * versions of CCM! */ public final static String - PROD_THEMES_BASE_DIR = THEMES_DIR + PROD_DIR_STUB; + PROD_THEMES_BASE_DIR = THEMES_DIR + PROD_DIR_STUB + "/"; /** Path stub into directory for production themes (sub-dir of THEMES_DIR). - * According to JavaEE spec with leading but without trailing "/"! */ + * According to JavaEE spec with leading "/", but deviating from the + * JavaEE spec we add a trailing "/" for backwards compatibility to + * versions of CCM! */ public final static String - DEV_THEMES_BASE_DIR = THEMES_DIR + DEV_DIR_STUB ; + DEV_THEMES_BASE_DIR = THEMES_DIR + DEV_DIR_STUB + "/"; - // ccm-themedirector (formerly ccm-ldn-theme) is no longer installed in its - // own web context (ROOT or ccm-ldn-theme/ccm-themedirector) so it is not - // needed anymore. We we want to install it in its own context again, we - // should find a way to determin the context from a central configuration. - // public final static String WEB_APP_NAME = "ROOT"; + // Developers NOTE: + // ================ + // We should consider to use the theme's url as entered by the user with + // a leading slash according to the specification and for sake of + // consistency within CCM. + // We would have to adjust the validation listener and the process listener + // in class ui/ThemeForm and to update the existing themes in the database. /** The location of the sync jsp used to sync up the multiple servers. */ public final static String SYNC_JSP = "sync-theme.jsp"; - /** This can be used to find the root webapp directory that is used - by default for most of the applications in CCM */ - public final static String ROOT_WEBAPP_PATH = "/ROOT"; - public static final String THEME_XML_PREFIX = "theme:"; public final static String XML_NS = "http://ccm.redhat.com/themedirector/1.0"; diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources.properties b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources.properties index 49be7ac6e..42831555a 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources.properties +++ b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources.properties @@ -32,3 +32,9 @@ theme.undo.default_style=Default Style theme.none=None theme.save=Save theme.set_default_theme=Default Theme +theme.title_hint=Enter the title of the theme, up to 80 characters. +theme.description_hint=Enter a short description for the theme, up to 4000 characters. +theme.url_hint=Enter the LAST part of the url for the theme, eg 'holiday'. Should NOT include a leading nor a trailing slash! +theme.save_button_hint=Save the details in the form +theme.cancel_button_hint=Abort changes & reset the form. +theme.cancel_button_pressed_msg=cancel pressed diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_de.properties b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_de.properties index 46fe59358..db835f2f1 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_de.properties +++ b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_de.properties @@ -32,3 +32,9 @@ theme.undo.default_style=Standard-Theme theme.none=Keines theme.save=Speichern theme.set_default_theme=Standard-Theme +theme.title_hint=Der Titel des Themes, kann bis zu 80 Zeichen lang sein. +theme.description_hint=Geben Sie eine kurze Beschreibung ein, bis zu 4000 Zeichen lang. +theme.url_hint=Geben Sie den LETZTEN Teil der URL ein, z.B. 'ferien'. Der Eintrag darf weder ein f\u00fchrendes noch ein abschlie\u00dfendes '/' enthalten! +theme.save_button_hint=Speichern der Angaben in dem Formular. +theme.cancel_button_hint=Verwerfen der Eintragungen und R\u00fccksetzen des Formulars. +theme.cancel_button_pressed_msg=Vorgang abgebrochen diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_en.properties b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_en.properties index 49be7ac6e..d04943938 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_en.properties +++ b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_en.properties @@ -32,3 +32,9 @@ theme.undo.default_style=Default Style theme.none=None theme.save=Save theme.set_default_theme=Default Theme +theme.title_hint=Enter the title of the theme, up to 80 characters. +theme.description_hint=Enter a short description for the theme, up to 4000 characters. +theme.url_hint=Enter the LAST part of tht url for the theme, eg 'holiday'. Should NOT include a leading nor a trailing slash! +theme.save_button_hint=Save the details in the form +theme.cancel_button_hint=Abort changes & reset the form. +theme.cancel_button_pressed_msg=cancel pressed diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_fr.properties b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_fr.properties index 49be7ac6e..d04943938 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_fr.properties +++ b/ccm-themedirector/src/com/arsdigita/themedirector/ThemeDirectorResources_fr.properties @@ -32,3 +32,9 @@ theme.undo.default_style=Default Style theme.none=None theme.save=Save theme.set_default_theme=Default Theme +theme.title_hint=Enter the title of the theme, up to 80 characters. +theme.description_hint=Enter a short description for the theme, up to 4000 characters. +theme.url_hint=Enter the LAST part of tht url for the theme, eg 'holiday'. Should NOT include a leading nor a trailing slash! +theme.save_button_hint=Save the details in the form +theme.cancel_button_hint=Abort changes & reset the form. +theme.cancel_button_pressed_msg=cancel pressed diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/ui/ThemeForm.java b/ccm-themedirector/src/com/arsdigita/themedirector/ui/ThemeForm.java index 13e6ab72a..3072f8490 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/ui/ThemeForm.java +++ b/ccm-themedirector/src/com/arsdigita/themedirector/ui/ThemeForm.java @@ -52,6 +52,8 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.DirectoryFileFilter; import org.apache.log4j.Logger; @@ -64,50 +66,68 @@ import org.apache.log4j.Logger; */ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstants { + /** Internal logger instance to faciliate debugging. Enable logging output + * by editing /WEB-INF/conf/log4j.properties int hte runtime environment + * and set com.arsdigita.themedirector.ui.ThemeForm=DEBUG by uncommenting + * or adding the line. */ private static final Logger s_log = Logger.getLogger(ThemeForm.class); - private ThemeSelectionModel m_theme; - private TextField m_title; - private TextArea m_description; - private TextField m_url; - private SaveCancelSection m_buttons; + private final ThemeSelectionModel m_theme; + private final TextField m_title; + private final TextArea m_description; + private final TextField m_url; + private final SaveCancelSection m_buttons; + /** + * Constructor creats the input form to create a new theme or edit an + * existing one. + * + * @param name + * @param theme + */ public ThemeForm(String name, ThemeSelectionModel theme) { super(name, new GridPanel(2)); setClassAttr("simpleThemeForm"); setRedirecting(true); - m_theme = theme; + m_theme = theme; // Initialize ThemeSelectionModel + // Add the Title input field add(new Label(GlobalizationUtil.globalize("theme.title"))); m_title = new TextField(new StringParameter("title")); + // Experimental. We are migrating the Label if a widget as part of the + // widgets's xml properties. + m_title.setLabel(GlobalizationUtil.globalize("theme.title")); m_title.addValidationListener(new NotEmptyValidationListener()); - m_title.setHint("Enter the title of the theme, up to 80 characters"); + m_title.setHint(GlobalizationUtil.globalize("theme.title_hint")); m_title.setSize(40); add(m_title); add(new Label(GlobalizationUtil.globalize("theme.description"))); m_description = new TextArea(new StringParameter("description")); + // Experimental, see above + m_description.setLabel(GlobalizationUtil.globalize("theme.description")); m_description.setCols(40); m_description.setRows(4); - m_description.setHint( - "Enter a short description for the theme, up to 4000 characters" - ); + m_description.setHint(GlobalizationUtil + .globalize("theme.description_hint")); add(m_description); add(new Label(GlobalizationUtil.globalize("theme.url"))); m_url = new TextField(new StringParameter("url")); + // Experimental, see above + m_url.setLabel(GlobalizationUtil.globalize("theme.url")); m_url.addValidationListener(new NotEmptyValidationListener()); m_title.setSize(40); - m_url.setHint( - "Enter the url for the theme, eg 'holiday'" - ); + m_url.setHint(GlobalizationUtil.globalize("theme.url_hint")); add(m_url); m_buttons = new SaveCancelSection(); - m_buttons.getSaveButton().setHint("Save the details in the form"); - m_buttons.getCancelButton().setHint("Abort changes & reset the form"); + m_buttons.getSaveButton().setHint(GlobalizationUtil + .globalize("theme.save_button_hint")); + m_buttons.getCancelButton().setHint(GlobalizationUtil + .globalize("theme.cancel_button_hint")); add(m_buttons); addSubmissionListener(new ThemeSubmissionListener()); @@ -116,23 +136,47 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan addValidationListener(new ThemeValidationListener()); } - // if this form is cancelled + /** + * Processed if this form is cancelled. + * + * @param s + * @return + */ + @Override public boolean isCancelled(PageState s) { return m_buttons.getCancelButton().isSelected(s); } + /** + * + */ private class ThemeSubmissionListener implements FormSubmissionListener { + + /** + * + * @param e + * @throws FormProcessException + */ + @Override public void submitted(FormSectionEvent e) - throws FormProcessException { + throws FormProcessException { PageState state = e.getPageState(); if (m_buttons.getCancelButton().isSelected(state)) { - throw new FormProcessException("cancel pressed"); + throw new FormProcessException( + "cancel pressed", + GlobalizationUtil.globalize("theme.cancel_button_hint") + ); } } } + /** + * Initializes the theme form with appropriate values if theme already + * exists. + */ private class ThemeInitListener implements FormInitListener { + @Override public void init(FormSectionEvent e) throws FormProcessException { PageState state = e.getPageState(); @@ -150,15 +194,34 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan } } + /** + * ProcessListener class to act upon the themedirector form input (after + * successful input validation if any). It's process method is the entry + * point. + */ private class ThemeProcessListener implements FormProcessListener { + + /** + * Process the form input data. The data are first stored into the + * database and than the file system is synced if required. In case of + * a new theme the default theme files (if existent) are copied. If for + * an existing theme the name (url) has changed, the filesystem + * directories are modified accordingly. + * + * @param e + * @throws FormProcessException + */ + @Override public void process(FormSectionEvent e) - throws FormProcessException { + throws FormProcessException { + PageState state = e.getPageState(); Theme theme = m_theme.getSelectedTheme(state); String oldURL = null; String newURL = null; if (theme == null) { + /* We handle a new (created) theme. No previous values exist.*/ newURL = (String)m_url.getValue(state); theme = new Theme((String)m_title.getValue(state), (String)m_description.getValue(state), @@ -171,7 +234,7 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan theme.setURL(newURL); } - // only add the theme if it is published + // only add to the theme if it is published if (theme.getLastPublishedUser() != null) { Subsite.getConfig().addTheme(theme.getURL(), theme.getTitle()); } @@ -180,24 +243,23 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan Subsite.getConfig().removeTheme(oldURL); } - theme.save(); + theme.save(); // save theme to database m_theme.setSelectedObject(state, theme); // now that the db part is done, we do the file IO File newDirectory = null; File oldDirectory = null; try { - // The WebAppRoot should be something like this: - // /var/ccm-devel/web///webapps/ccm-ldn-theme; + // Determine the WebAppRoot should be something like this: + // /var/ccm-devel/web///webapps/libreccm; File currentRoot = new File(Web.getServletContext().getRealPath("/")); newDirectory = new File(currentRoot, DEV_THEMES_BASE_DIR + newURL); if (newDirectory.exists() && !newURL.equals(oldURL)) { - // this means there is a file in the file system - // but not in the database - // this should never happen because "validate" should - // catch it. + // this means there is a file in the file system but not in + // the database this should never happen because "validate" + // should catch it. throw new UncheckedWrapperException ("The file " + newDirectory.getName() + " already " + "exists in the file system but not in the " + @@ -210,16 +272,17 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan oldURL); } - if (oldURL == null || !oldDirectory.exists()) { - // we make sure that the base directory exists and - // then we copy the files over. + if ( oldURL == null || !oldDirectory.exists()) { + // we make sure that the base directory exists and then we + // copy the files over. File baseDirectory = new File(currentRoot, DEV_THEMES_BASE_DIR); if (!baseDirectory.exists()) { baseDirectory.mkdirs(); } - copyDefaultFiles(newDirectory); + copyDefaultTheme(newDirectory,null); + // copyDefaultFiles(newDirectory); if (oldDirectory != null && !oldDirectory.exists()) { s_log.warn("We were asked to move files from " + @@ -251,8 +314,9 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan // the old directory...we need them to point to the // new directory DataCollection collection = - SessionManager.getSession().retrieve - (Site.BASE_DATA_OBJECT_TYPE); + SessionManager + .getSession() + .retrieve(Site.BASE_DATA_OBJECT_TYPE); collection.addEqualsFilter(Site.STYLE_DIRECTORY, oldURL); while (collection.next()) { @@ -270,10 +334,23 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan } } + /** + * ValöidatgionListener class to check the themedirector form input data. + * It's validate method is the entry point and executed when submitting + * the form. + */ private class ThemeValidationListener implements FormValidationListener { + + /** + * + * @param e + * @throws FormProcessException + */ + @Override public void validate(FormSectionEvent e) - throws FormProcessException { + throws FormProcessException { PageState state = e.getPageState(); + String url = (String)m_url.getValue(state); validateURLForm(state, url); validateURLUniqueness(state, url); @@ -298,6 +375,9 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan /** * This checks the form of the url...specifically, we are only allowing * [A-Z,a-z,0-9,_,-]. + * @param state + * @param url + * @throws com.arsdigita.bebop.FormProcessException */ public void validateURLForm(PageState state, String url) throws FormProcessException { @@ -316,13 +396,14 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan } } - - /** - * This makes sure no other theme has the same URL + * This makes sure no other theme has the same URL + * @param state + * @param url + * @throws com.arsdigita.bebop.FormProcessException */ public void validateURLUniqueness(PageState state, String url) - throws FormProcessException { + throws FormProcessException { if ( url != null ) { DataCollection collection = SessionManager.getSession() @@ -341,10 +422,39 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan } } + /** + * Copies a complete directory containing the default theme to a newly + * created theme's directory without any filtering or other processing. + * It assumes, that the source directory contains a complete and working + * set of theme files. + * + * @param newThemeDirectory specifies the target directory. Must not + * be null. + * @param defaultThemeDirectory Directory containing a complete set of + * theme files intended as default theme. + * If null the default theme directory is + * retrieved from ThemeDirector config + * + * @throws IOException + */ + private void copyDefaultTheme(File newThemeDirectory, + File defaultThemeDirectory) throws IOException { + if (defaultThemeDirectory == null) { + defaultThemeDirectory = new File( + Web.getServletContext().getRealPath("/") + + ThemeDirector.getConfig().getDefaultThemePath()); + } + + FileUtils.copyDirectory(defaultThemeDirectory, + newThemeDirectory); + } /** - * This copies the default theme files to the new directory that - * is specified by the pass in File + * Copies the default theme files to the new directory using a + * Manifest file to determine the files to copy. + * + * @param newDirectory specifies the target directory + * @throws IOException */ private void copyDefaultFiles(File newDirectory) throws IOException { ClassLoader loader = Thread.currentThread().getContextClassLoader(); @@ -358,7 +468,8 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan } if ( !newDirectory.mkdirs() ) { - throw new UncheckedWrapperException("Cannot create theme directory "+newDirectory.getAbsolutePath()); + throw new UncheckedWrapperException("Cannot create theme directory " + +newDirectory.getAbsolutePath()); } ManifestReader reader = @@ -367,10 +478,19 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan } + /** + * + */ private class FileWriterManifestReader extends ManifestReader { - private File m_newDirectory; - private String m_directoryFilter; + private final File m_newDirectory; + private final String m_directoryFilter; + /** + * Constructor. + * + * @param stream + * @param newDirectory + */ FileWriterManifestReader(InputStream stream, File newDirectory) { super(stream); m_newDirectory = newDirectory; @@ -378,6 +498,13 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan m_directoryFilter = ThemeDirector.getConfig().getDefaultThemePath(); } + /** + * + * @param is + * @param filePath + * @param isStyleFile + */ + @Override public void processManifestFileLine(InputStream is, String filePath, boolean isStyleFile) { diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/ui/ThemeValidationPanel.java b/ccm-themedirector/src/com/arsdigita/themedirector/ui/ThemeValidationPanel.java index c333d3a5d..e21773686 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/ui/ThemeValidationPanel.java +++ b/ccm-themedirector/src/com/arsdigita/themedirector/ui/ThemeValidationPanel.java @@ -42,6 +42,7 @@ import java.util.Collection; import java.util.Iterator; import com.arsdigita.templating.XSLTemplate; import com.arsdigita.templating.WrappedTransformerException; +import java.net.MalformedURLException; import javax.xml.transform.ErrorListener; import javax.xml.transform.TransformerException; @@ -49,14 +50,19 @@ import javax.xml.transform.TransformerException; import org.apache.log4j.Logger; /** - * This displays information about the results of running a validation - * test on all of the stylesheets for a given theme. It also includes - * links to "revalidate" and to return the viewing the theme. + * This displays information about the results of running a validation test + * on all of the stylesheets for a given theme. It also includes + * links to "revalidate" and to return the viewing the theme. * - * @author Randy Graebner <randyg@redhat.com> + * @author Randy Graebner <randyg@redhat.com> */ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { + /** Internal logger instance to faciliate debugging. Enable logging output + * by editing /WEB-INF/conf/log4j.properties int the runtime environment + * and set + * com.arsdigita.themedirector.ui.ThemeValidationPanel=DEBUG + * by uncommenting or adding the line. */ private static final Logger s_log = Logger.getLogger(ThemeValidationPanel.class); @@ -65,14 +71,16 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { RequestLocal m_listener; /** - * This creates a new validation panel + * This creates a new validation panel. + * * @param model This is the selection model so that the panel knows - * which theme to operation on + * which theme to operation on * @param container This is the parent container that holds this theme. - * When the user wants to quit validation and return to where they were, - * the visibility of the passed in container inverted (if it is visible - * then it becomes invisible, if it is invisible, it becomes visible) and - * this item becomes invisible. + * When the user wants to quit validation and return + * to where they were, the visibility of the passed in + * container inverted (if it is visible then it becomes + * invisible, if it is invisible, it becomes visible) and + * this item becomes invisible. */ // TODO: passing in the container is a hackish way to do the visibility... // is there a better way to do this? @@ -80,8 +88,8 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { SimpleComponent parentContainer) { super(1); m_model = model; - Label results = - new Label(GlobalizationUtil.globalize("theme.validation_results")); + Label results = new Label(GlobalizationUtil + .globalize("theme.validation_results")); results.setFontWeight(Label.BOLD); add(results); m_listener = new RequestLocal(); @@ -92,14 +100,16 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { new ActionLink(new Label(GlobalizationUtil.globalize ("theme.revalidate_theme"))); revalidateLink.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { validateStylesheets(e.getPageState()); } }); add(revalidateLink); - ToggleActionLink returnLink = new ToggleActionLink - (new Label(GlobalizationUtil.globalize("theme.return_to_previous"))); + ToggleActionLink returnLink = new ToggleActionLink(new Label( + GlobalizationUtil + .globalize("theme.return_to_previous"))); returnLink.addToggleComponent(this); returnLink.addToggleComponent(parentContainer); add(returnLink); @@ -112,7 +122,7 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { * class but this is more clear as to what is being done. */ private class ToggleActionLink extends ActionLink implements ActionListener { - private ArrayList m_components; + private final ArrayList m_components; ToggleActionLink(Label name) { super(name); @@ -120,6 +130,7 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { addActionListener(this); } + @Override public void actionPerformed(ActionEvent e) { Iterator iter = m_components.iterator(); PageState state = e.getPageState(); @@ -149,35 +160,45 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { /** - * This method sets up the validation by finding the correct base - * directory and setting up other necessary initialization variables + * This method sets up the validation by finding the correct base + * directory and setting up other necessary initialization variables. + * + * @param state + * @param listener */ private boolean validateStylesheets(PageState state, LoggingErrorListener listener) { - // this should verify the stylesheets and then present - // any error messages that are found + + /* Determine the theme to check */ Theme theme = m_model.getSelectedTheme(state); - - // The call to resolve returns a url similar to this: - // http://localhost:9008/resource/ccm-ldn-theme,ROOT/themes/heirloom/apps/theme/xsl/index.xs - // - // TODO: This is VERY UGLY! Bad style to code a path into source code! - // String base = "http://" + Web.getConfig().getHost().toString() + - // "/resource/ccm-ldn-theme/"; - String base = "http://" + Web.getConfig().getHost().toString() + - "/resource/ROOT/"; + + /* Determine the location in the servers file system */ File currentRoot = new File(Web.getServletContext().getRealPath("/")); - File devDir = new File(currentRoot, DEV_THEMES_BASE_DIR + - theme.getURL()); + File devDir = new File(currentRoot, + DEV_THEMES_BASE_DIR + theme.getURL() ); - // TODO: There has to be a better way to do this - String stylesheetPath = base + - DEV_THEMES_BASE_DIR + theme.getURL(); + /* Determine the URL to the stylesheets. Usually the URL is determined + by the templating system, based on a stylesheetPath.txt file + containing patterns to search for. + + Developer's Note: + We used to use a 'resource' tag involing a resource servlet to + deliver the correct file either from database or filesystem. Ir would + require an URL similar to + http://localhost:9008/libreccm/resource/themes/heirloom/apps/theme/xsl/index.xs + where librecms is the context ccm happpens to be installed in. + Currently we bypass the resource servlet and access the filesystem + directly. Must be modified as soon as we deliver the theme from db. */ + String stylesheetPath = "http://" + Web.getConfig().getHost().toString() + + Web.getWebappContextPath() + + DEV_THEMES_BASE_DIR + theme.getURL() ; if (s_log.isDebugEnabled()) { s_log.debug("Path is " + stylesheetPath); } + // this should verify the stylesheets and then present + // any error messages that are found checkFiles(devDir, stylesheetPath, listener); return !listener.hasErrors(); @@ -189,7 +210,8 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { * the entire folder subtree, loading every single available xsl * file. */ - private void checkFiles(File baseDirectory, String basePath, + private void checkFiles(File baseDirectory, + String basePath, ErrorListener listener) { File[] list = baseDirectory.listFiles(new XSLFileFilter()); if (list == null) { @@ -203,43 +225,39 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { // future caught errors. This is sort of a hack but I am not // sure of a better way around it boolean transformUsesListener = true; - - for (int i = 0; i < list.length; i ++) { - if (list[i].isDirectory()) { - // Only check top level XSL - the rest are xsl:import'd - //checkFiles(list[i], basePath + "/" + list[i].getName(), - // listener); + for (File list1 : list) { + if (list1.isDirectory()) { } else { - String filePath = basePath + "/" + list[i].getName(); + String filePath = basePath + "/" + list1.getName(); try { URL stylesheetURL = new URL(filePath); if (s_log.isDebugEnabled()) { s_log.debug("Validating " + stylesheetURL); } XSLTemplate template = new XSLTemplate(stylesheetURL, - listener); + listener); } catch (WrappedTransformerException we) { - if (transformUsesListener && - listener instanceof LoggingErrorListener && - !((LoggingErrorListener)listener).hasErrors()) { + if (transformUsesListener && + listener instanceof LoggingErrorListener && + !((LoggingErrorListener)listener).hasErrors()) { transformUsesListener = false; } if (!transformUsesListener) { - TransformerException transEx = - (TransformerException)we.getRootCause(); + TransformerException transEx = + (TransformerException)we.getRootCause(); try { listener.error(transEx); } catch (TransformerException transformerEx) { - s_log.error("Error logging the exception " + - transEx.getMessage(), - transformerEx); + s_log.error("Error logging the exception " + + transEx.getMessage(), + transformerEx); transEx.printStackTrace(); } } s_log.debug("Wrapper excpetion thrown"); - } catch (Exception exp) { - s_log.warn("Error creating template that was not a " + - " standard wrapper transformer exception.", + } catch (MalformedURLException exp) { + s_log.warn("Error creating template that was not a " + + "standard wrapper transformer exception.", exp); } } @@ -249,9 +267,10 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { /** * This is simply used so that only directories and xsl files are - * examined + * examined. */ private static class XSLFileFilter implements FileFilter { + @Override public boolean accept(File pathname) { return pathname.isDirectory() || pathname.getName().endsWith(".xsl"); @@ -260,8 +279,8 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { private class ValidationResults extends SimpleContainer { - private Label m_noErrorsLabel; - private Label m_errorsLabel; + private final Label m_noErrorsLabel; + private final Label m_errorsLabel; ValidationResults() { super(); @@ -299,8 +318,8 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants { /** - * if the collection size > 0 then it prints out the xml - * to display the messages + * If the collection size > 0 then it prints out the xml + * to display the messages. */ private void printMessages(String name, Element parent, Collection messages) { diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/ui/listeners/ApproveThemeActionListener.java b/ccm-themedirector/src/com/arsdigita/themedirector/ui/listeners/ApproveThemeActionListener.java index b07b7cabb..8022575f8 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/ui/listeners/ApproveThemeActionListener.java +++ b/ccm-themedirector/src/com/arsdigita/themedirector/ui/listeners/ApproveThemeActionListener.java @@ -44,23 +44,40 @@ import java.util.TreeSet; import org.apache.log4j.Logger; /** - * This approves the theme and pushes it to the production file location - * This action means that the user wants to approve the themes and - * push them live. This is done by copying the files from + * This approves the theme and pushes it to the production file location + * This action means that the user wants to approve the themes and + * push them live. This is done by copying the files from the devel directory + * into the published directory. * * @author Randy Graebner <randyg@redhat.com> */ -public class ApproveThemeActionListener implements ThemeDirectorConstants, ActionListener { +public class ApproveThemeActionListener implements ThemeDirectorConstants, + ActionListener { - private static final Logger s_log = - Logger.getLogger(ApproveThemeActionListener.class); + /** Internal logger instance to faciliate debugging. Enable logging output + * by editing /WEB-INF/conf/log4j.properties int the runtime environment + * and set + * com.arsdigita.themedirector.ui.listeners.ApproveThemeActionListener=DEBUG + * by uncommenting or adding the line. */ + private static final Logger s_log = Logger.getLogger( + ApproveThemeActionListener.class); - private ThemeSelectionModel m_model; + private final ThemeSelectionModel m_model; + /** + * Constructor, just stores the ThemeSelectionModel. + * + * @param model the ThemeSelectionModel + */ public ApproveThemeActionListener(ThemeSelectionModel model) { m_model = model; } + /** + * + * @param e + */ + @Override public void actionPerformed(ActionEvent e) { // First, we rename the current production directory // so that if there is an exception, we can try to move it diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/util/ManifestReader.java b/ccm-themedirector/src/com/arsdigita/themedirector/util/ManifestReader.java index b356dccf4..5813dc905 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/util/ManifestReader.java +++ b/ccm-themedirector/src/com/arsdigita/themedirector/util/ManifestReader.java @@ -19,32 +19,37 @@ package com.arsdigita.themedirector.util; +import com.arsdigita.themedirector.ThemeDirector; +import com.arsdigita.themedirector.ThemeDirectorConstants; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.web.Web; + import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.LineNumberReader; -import java.util.HashMap; -import com.arsdigita.web.Web; -import com.arsdigita.util.UncheckedWrapperException; import java.util.Collection; -import com.arsdigita.themedirector.ThemeDirector; -import com.arsdigita.themedirector.ThemeDirectorConstants; +import java.util.HashMap; import javax.servlet.ServletContext; + import org.apache.log4j.Logger; /** - * This is a utility class that will take in a manifest file and - * read it and then make calls to methods for each file that is found. - * In a typical usage, code will subclass this so that certain methods - * will write to different places. For instance, some code may - * override the "processManifestFileLine" to write the contents to - * the file system while another may write it to a zip file. + * This is a utility class that will take in a manifest file, read it, and + * then make calls to methods for each file that is found. + * In a typical usage, code will subclass this so that certain methods will + * write to different places. For instance, some code may override the + * "processManifestFileLine" to write the contents to the file system + * while another may write it to a zip file. */ public abstract class ManifestReader implements ThemeDirectorConstants { - private static final Logger s_log = - Logger.getLogger(ManifestReader.class); + /** Internal logger instance to faciliate debugging. Enable logging output + * by editing /WEB-INF/conf/log4j.properties int hte runtime environment + * and set com.arsdigita.themedirector.util.ManifestReader=DEBUG by + * uncommenting or adding the line. */ + private static final Logger s_log = Logger.getLogger(ManifestReader.class); private InputStream m_stream; private String m_fileName; @@ -52,8 +57,10 @@ public abstract class ManifestReader implements ThemeDirectorConstants { private HashMap m_actualContextList; /** - * This takes in the actual input stream that is the Manifest File - * so that the input stream can be correctly read + * This takes in the actual input stream that is the Manifest File + * so that the input stream can be correctly read. + * + * @param stream the input stream to read */ public ManifestReader(InputStream stream) { this(stream, null); @@ -64,6 +71,8 @@ public abstract class ManifestReader implements ThemeDirectorConstants { * so that the input stream can be correctly read. It also * takes in the fileName so that it can be used in error messages * if there is an error. + * @param stream the input stream to read + * @param fileName */ public ManifestReader(InputStream stream, String fileName) { this(stream, fileName, null); @@ -77,11 +86,11 @@ public abstract class ManifestReader implements ThemeDirectorConstants { * @param stream The input stream to read * @param fileName The name of the file we are reading that will * be displayed in the case of an error. - * @param possibleServletContext The servlet context to try to use - * when looking for files listed in the Manifest. This should be - * set when it is know that the manifest file specifies files - * that are located under a different webapps. If the - * file is not found under this context or this context is null + * @param possibleServletContext The servlet context to try to use when + * looking for files listed in the Manifest. This should be set + * when it is known that the manifest file specifies files that + * are located under a different webapps. If the file ist not + * found under this context or this context is null * then the file tries to use the default ServletContext */ public ManifestReader(InputStream stream, String fileName, @@ -94,13 +103,20 @@ public abstract class ManifestReader implements ThemeDirectorConstants { /** - * this is the name of the file that is being parsed. This will - * return null if the name has not been set + * Retrieves the name of the file that is being parsed. Will return null + * if the name has not been set. + * + * @return file name if set, otherwise null */ public String getFileName() { return m_fileName; } + /** + * Set the name of the file that is being parsed. + * + * @param fileName + */ public void setFileName(String fileName) { m_fileName = fileName; } @@ -112,17 +128,18 @@ public abstract class ManifestReader implements ThemeDirectorConstants { * this method will only really do anything once. */ public void processFile() { - LineNumberReader lines = - new LineNumberReader(new InputStreamReader(m_stream)); - Collection extensions = ThemeDirector.getConfig() - .getDownloadFileExtensions(); - try { - String line = lines.readLine(); - while (line != null) { - line = line.trim(); + LineNumberReader lines = new LineNumberReader( + new InputStreamReader(m_stream)); + Collection extensions = ThemeDirector.getConfig() + .getDownloadFileExtensions(); - int fileExtensionIndex = line.lastIndexOf("."); + try { + String line = lines.readLine(); + while (line != null) { + line = line.trim(); + + int fileExtensionIndex = line.lastIndexOf("."); // We check the following things to set the boolean // indicating if it is a file that should @@ -134,70 +151,77 @@ public abstract class ManifestReader implements ThemeDirectorConstants { // extensions as specified in the config file // 4. the file starts with the default directory and is // not just the directory itself - boolean fileForDownload = fileExtensionIndex > -1 && - line.length() > (fileExtensionIndex+1) && - extensions.contains(line.substring(fileExtensionIndex+1) - .toLowerCase()); + boolean fileForDownload = fileExtensionIndex > -1 + && line.length() > (fileExtensionIndex+1) + && extensions.contains(line.substring(fileExtensionIndex+1) + .toLowerCase()); - // get the stream from the WAR or file system - InputStream stream = - getResourceAsStream(line, m_possibleServletContext); + // get the stream from the WAR or file system + InputStream stream = getResourceAsStream(line, + m_possibleServletContext); - if (stream == null) { - s_log.debug - (m_fileName + ": " + - lines.getLineNumber() + - ": no such resource '" + line + "'"); - } else { - processManifestFileLine(stream, line, fileForDownload); - - stream.close(); - } - line = lines.readLine(); - } - } catch (IOException e) { - throw new UncheckedWrapperException - ("Error with " + m_fileName + ": " + - lines.getLineNumber(), e); - } finally { - try { - m_stream.close(); - } - catch (IOException e) { - throw new UncheckedWrapperException(e); + if (stream == null) { + s_log.debug(m_fileName + ": " + lines.getLineNumber() + + ": no such resource '" + line + "'"); + } else { + processManifestFileLine(stream, line, fileForDownload); + stream.close(); } + line = lines.readLine(); } + } catch (IOException e) { + throw new UncheckedWrapperException("Error with " + m_fileName + + ": " + + lines.getLineNumber(), e); + } finally { + try { + m_stream.close(); + } + catch (IOException e) { + throw new UncheckedWrapperException(e); + } + } } /** - * This provides a way for child classes to look for the resource - * in multiple places. By default, it only looks in the ServletContext + * This provides a way for child classes to look for the resource in + * multiple places. By default, it only looks in the ServletContext. + * + * @param line + * @param possibleServletContext + * @return stream, may be null */ protected InputStream getResourceAsStream(String line, String possibleServletContext) { InputStream stream = null; + if (possibleServletContext != null) { - stream = Web.getServletContext().getContext(possibleServletContext) - .getResourceAsStream(line); + stream = Web.getServletContext() // gets the servlet context of + // the current thread + .getContext(possibleServletContext) + .getResourceAsStream(line); } if (stream != null) { - setActualContext - (line, - Web.getServletContext().getContext(possibleServletContext)); + setActualContext(line, + Web.getServletContext() + .getContext(possibleServletContext)); } else { - stream = Web.getServletContext().getResourceAsStream(line); + stream = Web.getServletContext() // servlet ctx of actual thread + .getResourceAsStream(line); if (stream == null) { // this means that the file is not under the passed in - // context or the default context so let's check the "ROOT" + // context nor the default context so let's check the "ROOT" // context - stream = Web.getServletContext().getContext(ROOT_WEBAPP_PATH) - .getResourceAsStream(line); - if (stream != null) { - setActualContext(line, Web.getServletContext() - .getContext(ROOT_WEBAPP_PATH)); - } + // DEPRECATED. CCM may be installed at any context, in many + // cases dedicatedly no longer in ROOT + // stream = Web.getServletContext().getContext(ROOT_WEBAPP_PATH) + // .getResourceAsStream(line); + // if (stream != null) { + // setActualContext(line, Web.getServletContext() + // .getContext(ROOT_WEBAPP_PATH)); + // } } else { setActualContext(line, Web.getServletContext()); } @@ -207,14 +231,22 @@ public abstract class ManifestReader implements ThemeDirectorConstants { /** - * This provides subclasses with access to the actual ServletContext - * where the line is found. The info for the line should be available - * when processManifestFileLine is called for a given line + * This provides subclasses with access to the actual ServletContext + * where the line is found. The info for the line should be available + * when processManifestFileLine is called for a given line. + * + * @param line + * @return */ protected ServletContext getActualContext(String line) { return (ServletContext)m_actualContextList.get(line); } + /** + * + * @param line + * @param context + */ protected void setActualContext(String line, ServletContext context) { m_actualContextList.put(line, context); } diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/util/ThemeDevelopmentFileManager.java b/ccm-themedirector/src/com/arsdigita/themedirector/util/ThemeDevelopmentFileManager.java index 312887d47..15fe09e62 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/util/ThemeDevelopmentFileManager.java +++ b/ccm-themedirector/src/com/arsdigita/themedirector/util/ThemeDevelopmentFileManager.java @@ -40,8 +40,12 @@ import org.apache.log4j.Logger; */ public class ThemeDevelopmentFileManager extends ThemeFileManager { - private static Logger s_log = - Logger.getLogger(ThemeDevelopmentFileManager.class); + /** Internal logger instance to faciliate debugging. Enable logging output + * by editing /WEB-INF/conf/log4j.properties int hte runtime environment + * and set com.arsdigita.themedirector.util.ThemeDevelopmentFileManager=DEBUG + * by uncommenting or adding the line. */ + private static Logger s_log = Logger + .getLogger(ThemeDevelopmentFileManager.class); // The code in this class borrows heavily from // com.arsdigita.cms.publishToFile.FileManager @@ -51,13 +55,16 @@ public class ThemeDevelopmentFileManager extends ThemeFileManager { /** * Constructor just delegates to super class. + * * @param startupDelay * @param pollDelay * @param baseDirectory */ protected ThemeDevelopmentFileManager(int startupDelay, int pollDelay, String baseDirectory) { - super(s_log, startupDelay, pollDelay, baseDirectory); + + super(s_log, // Injects it's own logger + startupDelay, pollDelay, baseDirectory); // to the parent class methods! } diff --git a/ccm-themedirector/src/com/arsdigita/themedirector/util/ThemeFileManager.java b/ccm-themedirector/src/com/arsdigita/themedirector/util/ThemeFileManager.java index 8bfe3e676..4510f9e35 100755 --- a/ccm-themedirector/src/com/arsdigita/themedirector/util/ThemeFileManager.java +++ b/ccm-themedirector/src/com/arsdigita/themedirector/util/ThemeFileManager.java @@ -16,16 +16,12 @@ package com.arsdigita.themedirector.util; import com.arsdigita.themedirector.Theme; -import com.arsdigita.themedirector.ThemeDirector; import com.arsdigita.themedirector.ThemeCollection; import com.arsdigita.themedirector.ThemeDirectorConstants; import com.arsdigita.themedirector.ThemeFileCollection; import com.arsdigita.persistence.SessionManager; import com.arsdigita.persistence.TransactionContext; import com.arsdigita.themedirector.dispatcher.InternalThemePrefixerServlet; -import com.arsdigita.web.Application; -import com.arsdigita.web.ApplicationCollection; -import com.arsdigita.web.Web; import java.io.File; import java.io.FileOutputStream; @@ -38,8 +34,9 @@ import org.apache.log4j.Logger; /** - * Class for polling the database to look for new/updated files in - * the ThemeFile table. + * Class providing client classes (as FileManager for development and published + * themes) with base methods for polling the database to look for new/updated + * files in the ThemeFile table. * * For "published" files, It goes through each Theme and looks at the * last time it was published. If the last time published > last @@ -52,15 +49,14 @@ import org.apache.log4j.Logger; * then it writes out the new file. If the timestamp on the file system * is newer, it ignores the file. * - * * @author Randy Graebner - * * @version $Revision: #2 $ $DateTime: 2004/01/30 17:24:49 $ */ public abstract class ThemeFileManager extends Thread implements ThemeDirectorConstants { - /** Internal logger instance to faciliate debugging */ + /** Internal logger instance to faciliate debugging. Carries over the + * logger instance from the client child which actually does the work. */ private final Logger m_log; // The code in this class borrows heavily from @@ -89,8 +85,10 @@ public abstract class ThemeFileManager extends Thread * @param pollDelay * @param baseDirectory */ - protected ThemeFileManager(Logger log, int startupDelay, int pollDelay, - String baseDirectory) { + protected ThemeFileManager(Logger log, + int startupDelay, + int pollDelay, + String baseDirectory) { m_log = log; m_startupDelay = startupDelay; m_pollDelay = pollDelay; @@ -122,6 +120,7 @@ public abstract class ThemeFileManager extends Thread * Watch file for entries to process. The main routine that starts * file processing. */ + @Override public void run() { m_log.info("Start polling file in " + m_startupDelay + "s."); if (m_lastRunDate == null) { @@ -129,7 +128,7 @@ public abstract class ThemeFileManager extends Thread sleepSeconds(m_startupDelay); } m_log.info("Polling file every " + m_pollDelay + "s."); - while ( (sleepSeconds(m_pollDelay) || m_ignoreInterrupt) + while ( (sleepSeconds(m_pollDelay) || m_ignoreInterrupt) && m_keepWatchingFiles ) { // Get the last run date before we do anything, // so we can be sure that we do not miss any themes @@ -186,6 +185,8 @@ public abstract class ThemeFileManager extends Thread * This allows an outside piece of code to force an automatic update * on a single theme instead of making it wait for the thread to wake * up. + * + * @param theme */ public void updateThemeNow(Theme theme) { updateTheme(theme); @@ -194,13 +195,15 @@ public abstract class ThemeFileManager extends Thread /** - * This returns the base directory to use when writing out files - * THIS IS A HACK BECAUSE IT REQUIRES A SERVER TO BE RUNNING + * This returns the base directory to use when writing out files. + * THIS IS A HACK BECAUSE IT REQUIRES A SERVER TO BE RUNNING. + * + * @return */ protected String getBaseDirectory() { if (m_baseDirectory == null) { - // Because the constructor sets the base deirectory this should + // Because the constructor sets the base directory this should // never happen, but just in case .... // ThemeDirector may execute in a different web application context // as core oder CMS. To determine the actual context we may ask @@ -231,13 +234,18 @@ public abstract class ThemeFileManager extends Thread } /** - * this typically returns something like "getBaseDirectory() + PUB_DIR" + * This typically returns something like "getBaseDirectory() + PUB_DIR". + * + * @return */ protected abstract String getManagerSpecificDirectory(); /** - * This allows subclasses to filter the collection as appropriate - * (e.g. only return "live" files or only "draft" files). + * This allows subclasses to filter the collection as appropriate. + * (e.g. only return "live" files or only "draft" files). + * + * @param theme + * @return */ protected abstract ThemeFileCollection getThemeFilesCollection(Theme theme); @@ -268,9 +276,11 @@ public abstract class ThemeFileManager extends Thread */ /** - * This looks at all of the files in the db for the passed in theme - * and makes sure that this servers file system has all of the - * updated files. + * This looks at all of the files in the db for the passed in theme + * and makes sure that this servers file system has all of the + * updated files. + * + * @param theme */ protected void updateTheme(Theme theme) { String stub = getManagerSpecificDirectory(); @@ -329,7 +339,10 @@ public abstract class ThemeFileManager extends Thread /*** - * Sleep for n seconds + * Sleep for n seconds. + * + * @param n + * @return ***/ protected boolean sleepSeconds(long n) { try {