Fixed problem with copying default theme to new theme's directory. Added Label as Widget attribute (but not evaluated by Mandalay theme engine). Still a NPE if theme evaluation detects an error, e.g. duplicate theme url.

git-svn-id: https://svn.libreccm.org/ccm/trunk@2681 8810af33-2d31-482b-a856-94f89814c4df
master
pb 2014-06-07 14:44:09 +00:00
parent e4823ae780
commit 6c272debdd
15 changed files with 635 additions and 342 deletions

View File

@ -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);
/**

View File

@ -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!
* <listener>
@ -48,25 +49,31 @@ import org.apache.log4j.PropertyConfigurator;
* com.arsdigita.runtime.CCMApplicationContextListener
* </listener-class>
* </listener>
* 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.");

View File

@ -115,8 +115,7 @@ public class Web {
/**
* Gets the servlet request object of the current thread.
*
* @return The current <code>HttpServletRequest</code>; it can be
* null
* @return The current <code>HttpServletRequest</code>; 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 <code>ServletContext</code>; it can be null
*
*/
public static ServletContext getServletContext() {
return (ServletContext) s_servletContext.get();

View File

@ -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);
@ -171,89 +255,7 @@ 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);
}
}

View File

@ -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";

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {
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 {
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/<username>/<projectname>/webapps/ccm-ldn-theme;
// Determine the WebAppRoot should be something like this:
// /var/ccm-devel/web/<username>/<projectname>/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 {
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,10 +396,11 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
}
}
/**
* 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 {
@ -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) {

View File

@ -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
* 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 &lt;randyg@redhat.com&gt;
*/
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,13 +71,15 @@ 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
* @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
* 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...
@ -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();
@ -150,34 +161,44 @@ 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
* 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,14 +225,10 @@ 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()) {
@ -237,9 +255,9 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
}
}
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) {

View File

@ -46,21 +46,38 @@ 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
* push them live. This is done by copying the files from the devel directory
* into the published directory.
*
* @author Randy Graebner &lt;randyg@redhat.com&gt;
*/
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

View File

@ -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;
@ -53,7 +58,9 @@ public abstract class ManifestReader implements ThemeDirectorConstants {
/**
* This takes in the actual input stream that is the Manifest File
* so that the input stream can be correctly read
* 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,8 +128,9 @@ 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));
LineNumberReader lines = new LineNumberReader(
new InputStreamReader(m_stream));
Collection extensions = ThemeDirector.getConfig()
.getDownloadFileExtensions();
@ -134,31 +151,28 @@ 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)
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);
InputStream stream = getResourceAsStream(line,
m_possibleServletContext);
if (stream == null) {
s_log.debug
(m_fileName + ": " +
lines.getLineNumber() +
": no such resource '" + line + "'");
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);
throw new UncheckedWrapperException("Error with " + m_fileName
+ ": "
+ lines.getLineNumber(), e);
} finally {
try {
m_stream.close();
@ -171,33 +185,43 @@ public abstract class ManifestReader implements ThemeDirectorConstants {
/**
* 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)
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());
}
@ -209,12 +233,20 @@ 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
* 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);
}

View File

@ -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!
}

View File

@ -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 <a href="mailto:randyg@redhat.com">Randy Graebner</a>
*
* @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,7 +85,9 @@ public abstract class ThemeFileManager extends Thread
* @param pollDelay
* @param baseDirectory
*/
protected ThemeFileManager(Logger log, int startupDelay, int pollDelay,
protected ThemeFileManager(Logger log,
int startupDelay,
int pollDelay,
String baseDirectory) {
m_log = log;
m_startupDelay = startupDelay;
@ -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) {
@ -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
* 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);
@ -271,6 +279,8 @@ 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.
*
* @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 {