/*
* The contents of this file are subject to the Mozilla Public License Version 1.1
* (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at .
*
* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
* language governing rights and limitations under the License.
*
* The Original Code is the Venice Web Communities System.
*
* The Initial Developer of the Original Code is Eric J. Bowersox ,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
*
* Contributor(s):
*/
package com.silverwrist.venice.servlets.format;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.parsers.*;
import org.apache.log4j.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.silverwrist.util.DOMElementHelper;
import com.silverwrist.util.IOUtil;
import com.silverwrist.util.StringUtil;
import com.silverwrist.venice.core.AccessError;
import com.silverwrist.venice.core.ConfigException;
import com.silverwrist.venice.core.DataException;
import com.silverwrist.venice.core.UserContext;
import com.silverwrist.venice.core.VeniceEngine;
import com.silverwrist.venice.servlets.Variables;
import com.silverwrist.venice.servlets.format.menus.LeftMenu;
import com.silverwrist.venice.servlets.format.sideboxes.SideBoxFactory;
public class RenderConfig implements ColorSelectors
{
/*--------------------------------------------------------------------------------
* Static data values
*--------------------------------------------------------------------------------
*/
protected static final String ATTR_NAME = "com.silverwrist.venice.servlets.RenderConfig";
protected static final String CONFIG_FILE_PARAM = "render.config";
private static Category logger = Category.getInstance(RenderConfig.class);
private static Map colornames_map;
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
*/
private Document config;
private String site_title;
private boolean want_comments;
private boolean allow_gzip;
private boolean no_smart_tags;
private String font_face;
private File stylesheet;
private long stylesheet_time = 0;
private String image_url;
private String static_url;
private String site_logo;
private int site_logo_width = 140;
private int site_logo_height = 80;
private String site_logo_linkURL = null;
private HashMap stock_messages;
private HashMap menus;
private String[] colors_array;
private int footer_logo_scale;
private Map sidebox_factories;
/*--------------------------------------------------------------------------------
* Constructor
*--------------------------------------------------------------------------------
*/
protected RenderConfig(String config_file, String root_file_path) throws ConfigException
{
config = loadConfiguration(config_file);
// Make sure the configuration is valid...
Element root = config.getDocumentElement();
if (!(root.getTagName().equals("render-config")))
{ // not the correct root tag name
logger.fatal("config document is not a document (root tag: <"
+ root.getTagName() + "/>)");
throw new ConfigException("document is not a document",root);
} // end if
// Get the site name.
DOMElementHelper root_h = new DOMElementHelper(root);
site_title = root_h.getSubElementText("site-name");
if (site_title==null)
{ // no section - bail out now!
logger.fatal("config document has no element");
throw new ConfigException("no section found in config file",root);
} // end if
if (logger.isDebugEnabled())
logger.debug("Site title: " + site_title);
Element render_sect = root_h.getSubElement("rendering");
if (render_sect==null)
{ // no section - bail out now!
logger.fatal("config document has no section");
throw new ConfigException("no section found in config file",root);
} // end if
DOMElementHelper render_sect_h = new DOMElementHelper(render_sect);
want_comments = render_sect_h.hasChildElement("html-comments");
allow_gzip = render_sect_h.hasChildElement("gzip-output");
no_smart_tags = !(render_sect_h.hasChildElement("ms-copyright-violations"));
if (logger.isDebugEnabled())
{ // log the read values
logger.debug("Use HTML comments: " + String.valueOf(want_comments));
logger.debug("Use GZIP encoding: " + String.valueOf(allow_gzip));
logger.debug("Disable IE Smart Tags: " + String.valueOf(no_smart_tags));
} // end if
font_face = render_sect_h.getSubElementText("font");
if (font_face==null)
{ // no tag - bail out now!
logger.fatal(" section has no element");
throw new ConfigException("no found in section",render_sect);
} // end if
if (logger.isDebugEnabled())
logger.debug("Font face: " + font_face);
String stylesheet_loc = render_sect_h.getSubElementText("stylesheet");
if (stylesheet_loc!=null)
{ // we're using Cascading Stylesheets - load it and test for existence
if (!(stylesheet_loc.startsWith("/")))
stylesheet_loc = root_file_path + stylesheet_loc;
if (logger.isDebugEnabled())
logger.debug("Stylesheet location: " + stylesheet_loc);
// Test to make sure the stylesheet is actually present.
stylesheet = new File(stylesheet_loc);
if (!(stylesheet.exists() && stylesheet.canRead()))
{ // it's not there - bail out!
logger.fatal("unable to read stylesheet file: " + stylesheet_loc);
throw new ConfigException("stylesheet " + stylesheet_loc + " cannot be read",render_sect);
} // end if
} // end if
else // no stylesheet
stylesheet = null;
Element colors_sect = render_sect_h.getSubElement("colors");
if (colors_sect==null)
{ // no tag - bail out now!
logger.fatal(" section has no element");
throw new ConfigException("no found in section",render_sect);
} // end if
colors_array = new String[colornames_map.size()];
int i;
NodeList colors_nlist = colors_sect.getChildNodes();
for (i=0; i section - bail out now!
logger.fatal("config document has no section");
throw new ConfigException("no section found in config file",root);
} // end if
DOMElementHelper paths_sect_h = new DOMElementHelper(paths_sect);
image_url = paths_sect_h.getSubElementText("image");
if (image_url==null)
{ // no tag - bail out now!
logger.fatal(" section has no element");
throw new ConfigException("no found in section",paths_sect);
} // end if
if (logger.isDebugEnabled())
logger.debug("Image path: " + image_url);
static_url = paths_sect_h.getSubElementText("static");
if (static_url==null)
{ // no tag - bail out now!
logger.fatal(" section has no element");
throw new ConfigException("no found in section",paths_sect);
} // end if
if (logger.isDebugEnabled())
logger.debug("Static files path: " + static_url);
Element site_logo_elt = paths_sect_h.getSubElement("site-logo");
if (site_logo_elt==null)
{ // no tag - bail out now!
logger.fatal(" section has no element");
throw new ConfigException("no found in section",paths_sect);
} // end if
DOMElementHelper site_logo_h = new DOMElementHelper(site_logo_elt);
site_logo = site_logo_h.getElementText();
if (site_logo==null)
{ // no site logo specified - bail out now!
logger.fatal(" section has no site logo element");
throw new ConfigException("no site logo found in section",paths_sect);
} // end if
// get logo width and height
Integer fooint = site_logo_h.getAttributeInt("width");
if (fooint!=null)
site_logo_width = fooint.intValue();
fooint = site_logo_h.getAttributeInt("height");
if (fooint!=null)
site_logo_height = fooint.intValue();
// get logo link URL
String tmp = site_logo_elt.getAttribute("href");
if (!(StringUtil.isStringEmpty(tmp)))
site_logo_linkURL = tmp;
if (logger.isDebugEnabled())
logger.debug("Site logo: " + site_logo);
// Load the sidebox configuration information.
String sidebox_config = paths_sect_h.getSubElementText("sidebox-config");
if (sidebox_config==null)
{ // the sidebox config was not present
logger.fatal(" section has no element");
throw new ConfigException("no found in section",paths_sect);
} // end if
if (!(sidebox_config.startsWith("/")))
sidebox_config = root_file_path + sidebox_config;
Document sb_doc = loadConfiguration(sidebox_config);
Element sb_root = sb_doc.getDocumentElement();
if (!(sb_root.getTagName().equals("sidebox-config")))
{ // the sidebox configuration file isn't the right type
logger.fatal("config document is not a document (root tag: <"
+ sb_root.getTagName() + "/>)");
throw new ConfigException("document is not a document",sb_root);
} // end if
NodeList sb_nodes = sb_root.getChildNodes();
HashMap tmp_factories = new HashMap();
for (i=0; i specified with no ID!");
throw new ConfigException("no ID specified in ",sb);
} // end if
// convert to an integer which we'll be using later in the HashMap
Integer id = new Integer(tmp);
// get the name of the factory class
DOMElementHelper sb_h = new DOMElementHelper(sb);
tmp = sb_h.getSubElementText("factory-class");
if (StringUtil.isStringEmpty(tmp))
{ // the factory class was not specified!
logger.fatal(" config incomplete (missing )!");
throw new ConfigException("configuration of is not complete!",sb);
} // end if
Class factory_class = Class.forName(tmp);
SideBoxFactory factory = (SideBoxFactory)(factory_class.newInstance());
factory.setConfiguration(sb);
tmp_factories.put(id,factory);
} // end if
catch (NumberFormatException e1)
{ // ID value was not an integer
logger.fatal(" ID not numeric!");
throw new ConfigException("non-numeric ID specified in ",sb);
} // end catch
catch (ClassNotFoundException e2)
{ // sidebox could not be configured
logger.fatal(" config not valid!",e2);
throw new ConfigException(" in is not a valid class!",sb);
} // end catch
catch (IllegalAccessException e3)
{ // could not access class and/or constructor
logger.fatal(" is not accessible!",e3);
throw new ConfigException(" in is not accessible!",sb);
} // end catch
catch (InstantiationException e4)
{ // unable to create the class
logger.fatal(" could not be instantiated!",e4);
throw new ConfigException(" in could not be created!",sb);
} // end catch
catch (ClassCastException e5)
{ // the class is not a SideBoxFactory implementor - cannot create
logger.fatal(" is not a SideBoxFactory!",e5);
throw new ConfigException(" in is not the correct type!",sb);
} // end catch
} // end if
} // end for
sidebox_factories = Collections.unmodifiableMap(tmp_factories);
Element msg_sect = root_h.getSubElement("messages");
if (msg_sect==null)
{ // no section - bail out now!
logger.fatal("config document has no section");
throw new ConfigException("no section found in config file",root);
} // end if
// Initialize the stock messages list.
stock_messages = new HashMap();
NodeList msg_nodes = msg_sect.getChildNodes();
for (i=0; i section - bail out now!
logger.fatal("config document has no section");
throw new ConfigException("no section found in config file",root);
} // end if
// Initialize the menus list.
menus = new HashMap();
NodeList menu_nodes = menu_sect.getChildNodes();
for (i=0; i subnodes and use them to initialize menus
Node mn = menu_nodes.item(i);
if (mn.getNodeType()==Node.ELEMENT_NODE)
{ // found an element - now check it's name
if (mn.getNodeName().equals("menudef"))
{ // root of a menu definition - get its ID, build it, and save it
Element mel = (Element)mn;
String menuid = mel.getAttribute("id");
if (menuid==null)
{ // no menu ID attribute
logger.fatal(" seen with no \"id\" attribute");
throw new ConfigException(" seen with no \"id\" attribute",mel);
} // end if
// create the menu and add it to the mapping
LeftMenu menu = new LeftMenu(mel,menuid);
menus.put(menuid,menu);
if (logger.isDebugEnabled())
logger.debug("menu \"" + menuid + "\" defined");
} // end if (found the root of a menu definition)
else
{ // unknown element - bail out!
logger.fatal("config document has unknown node <" + mn.getNodeName() +
"/> inside ");
throw new ConfigException("unknown node name <" + mn.getNodeName() + "/> in ",
menu_sect);
} // end else
} // end if
} // end for
if (logger.isDebugEnabled())
logger.debug(menus.size() + " menu definitions loaded from config");
} // end constructor
/*--------------------------------------------------------------------------------
* Internal functions
*--------------------------------------------------------------------------------
*/
private static Document loadConfiguration(String configname) throws ConfigException
{
try
{ // create a simple DOM parser by using the Java XML parsing API
DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
fac.setNamespaceAware(false);
fac.setValidating(false);
DocumentBuilder parser = fac.newDocumentBuilder();
// access the config file and parse it into our config data tree
File configfile = new File(configname);
Document rc = parser.parse(configfile);
if (logger.isDebugEnabled())
logger.debug("configuration loaded successfully");
return rc;
} // end try
catch (FactoryConfigurationError e1)
{ // if the document builder factory could not be created
logger.fatal("Parser factory configuration error: " + e1.getMessage(),e1);
throw new ConfigException("XML parser factory could not be created - " + e1.getMessage());
} // end catch
catch (ParserConfigurationException e2)
{ // if the XML parser itself could not be created
logger.fatal("Parser configuration error: " + e2.getMessage(),e2);
throw new ConfigException("XML parser could not be created - " + e2.getMessage(),e2);
} // end catch
catch (SAXException e3)
{ // if the XML parser choked on our document
if (e3 instanceof SAXParseException)
{ // we have a detailed message - make a proper exception
SAXParseException e3a = (SAXParseException)e3;
logger.fatal("Config file error [" + configname + ":" + e3a.getLineNumber() + ","
+ e3a.getColumnNumber() + "]: " + e3a.getMessage(),e3a);
throw new ConfigException("Configuration file error: " + e3a.getMessage() + " at line "
+ e3a.getLineNumber() + ", column " + e3a.getColumnNumber(),e3a);
} // end if
else
{ // generic exception - just send up a simple error message
logger.fatal("Config file error [" + configname + "]: " + e3.getMessage(),e3);
throw new ConfigException("Configuration file error - " + e3.getMessage(),e3);
} // end else
} // end catch
catch (IOException e4)
{ // error reading the config file itself off the disk
logger.fatal("IO error reading config: " + e4.getMessage(),e4);
throw new ConfigException("unable to read config file \"" + configname + "\" - " + e4.getMessage(),e4);
} // end catch
} // end loadConfiguration
/*--------------------------------------------------------------------------------
* External operations usable only by RenderData
*--------------------------------------------------------------------------------
*/
boolean useHTMLComments()
{
return want_comments;
} // end useHTMLComments
boolean isGZIPAllowed()
{
return allow_gzip;
} // end isGZIPAllowed
boolean noSmartTags()
{
return no_smart_tags;
} // end noSmartTags
String getFullImagePath(String name)
{
StringBuffer buf = new StringBuffer();
buf.append(image_url).append(name);
return buf.toString();
} // end getFullImagePath
String getStaticFilePath(String name)
{
StringBuffer buf = new StringBuffer();
buf.append(static_url).append(name);
return buf.toString();
} // end getStaticFilePath
String getTitleTag(String specific)
{
StringBuffer buf = new StringBuffer();
buf.append("").append(specific).append(" - ").append(site_title).append("");
return buf.toString();
} // end getTitleTag
String getSiteImageTag(int hspace, int vspace)
{
StringBuffer buf = new StringBuffer();
if (site_logo_linkURL!=null)
buf.append("");
buf.append("
0)
buf.append(" HSPACE=").append(hspace);
if (vspace>0)
buf.append(" VSPACE=").append(vspace);
buf.append('>');
if (site_logo_linkURL!=null)
buf.append("");
return buf.toString();
} // end getSiteImageTag
String getStdFontTag(String color, int size)
{
StringBuffer buf = new StringBuffer("');
return buf.toString();
} // end getStdFontTag
String getStdFontTag(int selector, int size)
{
if ((selector<0) || (selector>=colors_array.length))
throw new IndexOutOfBoundsException("getStdColor(): invalid color selector value");
return getStdFontTag(colors_array[selector],size);
} // end getStdFontTag
String getStdBaseFontTag(int size)
{
StringBuffer buf = new StringBuffer("*");
return buf.toString();
} // end getRequiredBullet
void writeContentHeader(Writer out, String primary, String secondary) throws IOException
{
out.write("" + getStdFontTag(colors_array[CONTENT_HEADER],5) + ""
+ StringUtil.encodeHTML(primary) + "");
if (secondary!=null)
out.write(" " + getStdFontTag(colors_array[CONTENT_HEADER],3) + ""
+ StringUtil.encodeHTML(secondary) + "");
out.write("
\n");
} // end writeContentHeader
String getStockMessage(String identifier)
{
return (String)(stock_messages.get(identifier));
} // end getStockMessage
void writeStockMessage(Writer out, String identifier) throws IOException
{
String text = (String)(stock_messages.get(identifier));
out.write(StringUtil.encodeHTML(text));
} // end writeStockMessage
String getStdColor(int selector)
{
if ((selector<0) || (selector>=colors_array.length))
throw new IndexOutOfBoundsException("getStdColor(): invalid color selector value");
return colors_array[selector];
} // end getStdColor
int scaleFooterLogo(int param)
{
return (param * footer_logo_scale) / 100;
} // end scaleFooterLogo
public LeftMenu getLeftMenu(String identifier)
{
return (LeftMenu)(menus.get(identifier));
} // end getLeftMenu
synchronized String loadStyleSheetData() throws IOException
{
if (stylesheet==null)
return null;
// Load the stylesheet data.
StringBuffer raw_data = IOUtil.loadText(stylesheet);
stylesheet_time = stylesheet.lastModified();
// Set up the replacements map to replace the various parameters.
HashMap vars = new HashMap();
vars.put("font",font_face);
vars.put("color.frame",colors_array[FRAME_BACKGROUND]);
vars.put("color.top.background",colors_array[TITLE_BACKGROUND]);
vars.put("color.top.foreground",colors_array[TITLE_FOREGROUND]);
vars.put("color.top.link",colors_array[TITLE_LINK]);
vars.put("color.left.background",colors_array[LEFT_BACKGROUND]);
vars.put("color.left.foreground",colors_array[LEFT_FOREGROUND]);
vars.put("color.left.link",colors_array[LEFT_LINK]);
vars.put("color.content.background",colors_array[CONTENT_BACKGROUND]);
vars.put("color.content.foreground",colors_array[CONTENT_FOREGROUND]);
vars.put("color.content.header",colors_array[CONTENT_HEADER]);
vars.put("color.disabled",colors_array[CONTENT_DISABLED]);
vars.put("color.error",colors_array[CONTENT_ERROR]);
vars.put("color.sidebox.top.background",colors_array[SIDEBOX_TITLE_BACKGROUND]);
vars.put("color.sidebox.top.foreground",colors_array[SIDEBOX_TITLE_FOREGROUND]);
vars.put("color.sidebox.background",colors_array[SIDEBOX_CONTENT_BACKGROUND]);
vars.put("color.sidebox.foreground",colors_array[SIDEBOX_CONTENT_FOREGROUND]);
vars.put("color.sidebox.link",colors_array[SIDEBOX_CONTENT_LINK]);
vars.put("color.dlg.confirm.title.background",colors_array[CONFIRM_TITLE_BACKGROUND]);
vars.put("color.dlg.confirm.title.foreground",colors_array[CONFIRM_TITLE_FOREGROUND]);
vars.put("color.dlg.error.title.background",colors_array[ERROR_TITLE_BACKGROUND]);
vars.put("color.dlg.error.title.foreground",colors_array[ERROR_TITLE_FOREGROUND]);
return StringUtil.replaceAllVariables(raw_data.toString(),vars);
} // end loadStyleSheet
boolean hasStyleSheetChanged()
{
if (stylesheet==null)
return false;
return (stylesheet_time!=stylesheet.lastModified());
} // end hasStyleSheetChanged
boolean useStyleSheet()
{
return (stylesheet!=null);
} // end useStyleSheet
VeniceContent createSideBox(int id, VeniceEngine engine, UserContext uc) throws AccessError, DataException
{
SideBoxFactory fact = (SideBoxFactory)(sidebox_factories.get(new Integer(id)));
if (fact==null)
throw new DataException("invalid sidebox ID!");
return fact.create(engine,uc);
} // end createSideBox
/*--------------------------------------------------------------------------------
* Static operations for use by VeniceServlet
*--------------------------------------------------------------------------------
*/
public static RenderConfig getRenderConfig(ServletContext ctxt) throws ServletException
{
// Look in the servlet attributes first.
Object obj = ctxt.getAttribute(ATTR_NAME);
if (obj!=null)
return (RenderConfig)obj;
// Get the root file path.
String root_file_path = ctxt.getRealPath("/");
if (!(root_file_path.endsWith("/")))
root_file_path += "/";
// Get the parameter for the renderer's config file.
String cfgfile = ctxt.getInitParameter(CONFIG_FILE_PARAM);
if (!(cfgfile.startsWith("/")))
cfgfile = root_file_path + cfgfile;
logger.info("Initializing Venice rendering using config file: " + cfgfile);
try
{ // create the RenderConfig object and save it to attributes.
RenderConfig rconf = new RenderConfig(cfgfile,root_file_path);
ctxt.setAttribute(ATTR_NAME,rconf);
return rconf;
} // end try
catch (ConfigException e)
{ // configuration failed! post an error message
logger.fatal("Rendering configuration failed: " + e.getMessage(),e);
throw new ServletException("Venice rendering configuration failed: " + e.getMessage(),e);
} // end catch
} // end getRenderConfig
public static RenderData createRenderData(ServletContext ctxt, HttpServletRequest request,
HttpServletResponse response) throws ServletException
{
UserContext uc = Variables.getUserContext(ctxt,request,request.getSession(true));
return new RenderData(getRenderConfig(ctxt),uc,ctxt,request,response);
} // end createRenderData
public static RenderData createRenderData(ServletContext ctxt, UserContext uc, HttpServletRequest request,
HttpServletResponse response) throws ServletException
{
return new RenderData(getRenderConfig(ctxt),uc,ctxt,request,response);
} // end createRenderData
/*--------------------------------------------------------------------------------
* Static initializer
*--------------------------------------------------------------------------------
*/
static
{ // Initialize the mapping of color names from render-config.xml to color selector IDs.
HashMap m = new HashMap();
m.put("frame-bg",new Integer(FRAME_BACKGROUND));
m.put("title-bg",new Integer(TITLE_BACKGROUND));
m.put("title-fg",new Integer(TITLE_FOREGROUND));
m.put("title-link",new Integer(TITLE_LINK));
m.put("left-bg",new Integer(LEFT_BACKGROUND));
m.put("left-fg",new Integer(LEFT_FOREGROUND));
m.put("left-link",new Integer(LEFT_LINK));
m.put("content-bg",new Integer(CONTENT_BACKGROUND));
m.put("content-fg",new Integer(CONTENT_FOREGROUND));
m.put("content-hdr",new Integer(CONTENT_HEADER));
m.put("content-disabled",new Integer(CONTENT_DISABLED));
m.put("content-error",new Integer(CONTENT_ERROR));
m.put("sidebox-title-bg",new Integer(SIDEBOX_TITLE_BACKGROUND));
m.put("sidebox-title-fg",new Integer(SIDEBOX_TITLE_FOREGROUND));
m.put("sidebox-content-bg",new Integer(SIDEBOX_CONTENT_BACKGROUND));
m.put("sidebox-content-fg",new Integer(SIDEBOX_CONTENT_FOREGROUND));
m.put("sidebox-content-link",new Integer(SIDEBOX_CONTENT_LINK));
m.put("confirm-title-bg",new Integer(CONFIRM_TITLE_BACKGROUND));
m.put("confirm-title-fg",new Integer(CONFIRM_TITLE_FOREGROUND));
m.put("error-title-bg",new Integer(ERROR_TITLE_BACKGROUND));
m.put("error-title-fg",new Integer(ERROR_TITLE_FOREGROUND));
colornames_map = Collections.unmodifiableMap(m);
} // end static initializer
} // end class RenderConfig