From c3a9839ae561de7c0020de19445035e9dd9f16ee Mon Sep 17 00:00:00 2001
From: jensp
- * If this returns
- * This method returns
- * By default, a {@link PageErrorDisplay} component is used to display the validation errors.
+ * By default, a {@link PageErrorDisplay} component is used to display the
+ * validation errors.
*
- * @param c the component that will display the validation errors in the current
- *
- * By default, a {@link PageErrorDisplay} component is used to display the validation errors.
+ * By default, a {@link PageErrorDisplay} component is used to display the
+ * validation errors.
*
- * @return the component that will display the validation errors in the current
- *
* These values will ultimately wind up in a <link>
* tag in the head of the HTML page.
*
*
- * Note that the stylesheet set with this call has nothing to do with the XSLT stylesheet
- * (transformer) that is applied to the XML generated from this page!
+ * Note that the stylesheet set with this call has nothing to do with the
+ * XSLT stylesheet (transformer) that is applied to the XML generated from
+ * this page!
*
* @param styleSheetURI the location of the stylesheet
* @param mimeType the MIME type of the stylesheet, usually
@@ -510,13 +539,14 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Adds a global state parameter to this page. Global parameters are values that need to be
- * preserved between requests, but that have no special connection to any of the components on
- * the page. For a page that displays details about an item, a global parameter would be used to
- * identify the item.
+ * Adds a global state parameter to this page. Global parameters are values
+ * that need to be preserved between requests, but that have no special
+ * connection to any of the components on the page. For a page that displays
+ * details about an item, a global parameter would be used to identify the
+ * item.
*
- * If the parameter was previously added as a component state parameter, its name is unmangled
- * and stays unmangled.
+ * If the parameter was previously added as a component state parameter, its
+ * name is unmangled and stays unmangled.
*
* @see #addComponentStateParam
*
@@ -532,8 +562,8 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Constructs the top nodes of the DOM or JDOM tree. Used by generateXML(PageState, Document)
- * below.
+ * Constructs the top nodes of the DOM or JDOM tree. Used by
+ * generateXML(PageState, Document) below.
*
* Generates DOM fragment:
*
- * Locking a page helps in finding mistakes that result from modifying a page's structure.
*
*
* A typical Page may be created as follows: null
@@ -85,14 +94,16 @@ public class Page extends SimpleComponent implements Container {
*/
private static final String DELIMITER = ".";
/**
- * The prefix that gets prepended to all state variables. Components must not use variables
- * starting with this prefix. This guarantees that the page state and variables individual
- * components wish to pass do not interfere with each other.
+ * The prefix that gets prepended to all state variables. Components must
+ * not use variables starting with this prefix. This guarantees that the
+ * page state and variables individual components wish to pass do not
+ * interfere with each other.
*/
private static final String COMPONENT_PREFIX = "bbp" + DELIMITER;
private static final String INTERNAL = COMPONENT_PREFIX;
/**
- * The name of the special parameter that indicates which component has been selected.
+ * The name of the special parameter that indicates which component has been
+ * selected.
*/
static final String SELECTED = INTERNAL + "s";
static final String CONTROL_EVENT = INTERNAL + "e";
@@ -109,14 +120,15 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * The name of the request parameter used for the visibility state of components stored in
- * m_invisible.
+ * The name of the request parameter used for the visibility state of
+ * components stored in m_invisible.
*/
static final String INVISIBLE = INTERNAL + "i";
/**
- * Map of stateful components (id --> Component) SortedMap used because component based hash for
- * page is based on concatenation of component ids, and so need to guarantee that they are
- * returned in the same order for the same page - cg.
+ * Map of stateful components (id --> Component) SortedMap used because
+ * component based hash for page is based on concatenation of component ids,
+ * and so need to guarantee that they are returned in the same order for the
+ * same page - cg.
*/
private SortedMap m_componentMap;
private List m_components;
@@ -132,32 +144,35 @@ public class Page extends SimpleComponent implements Container {
private List m_actionListeners;
private List m_requestListeners;
/**
- * The title of the page to be added in the head of HTML output. The title is wrapped in a Label
- * to allow developers to add PrintListeners to dynamically change the value of the title.
+ * The title of the page to be added in the head of HTML output. The title
+ * is wrapped in a Label to allow developers to add PrintListeners to
+ * dynamically change the value of the title.
*/
private Label m_title;
/**
- * Stores the actual title for the current request. The title may be generated with a
- * PrintListener of the m_title Label.
+ * Stores the actual title for the current request. The title may be
+ * generated with a PrintListener of the m_title Label.
*/
private RequestLocal m_currentTitle;
/**
- * A list of all the client-side stylesheets. The elements of the list are of type
- * Page.Stylesheet, defined at the end of this file.
+ * A list of all the client-side stylesheets. The elements of the list are
+ * of type Page.Stylesheet, defined at the end of this file.
*/
private List m_clientStylesheets;
private StringParameter m_selected;
private StringParameter m_controlEvent;
private StringParameter m_controlValue;
/**
- * The default (initial) visibility of components. The encoding is identical to that for
- * PageState.m_invisible.
+ * The default (initial) visibility of components. The encoding is identical
+ * to that for PageState.m_invisible.
*
- * This variable is package-friendly since it needs to be accessed by PageState.
+ * This variable is package-friendly since it needs to be accessed by
+ * PageState.
*/
protected BitSet m_invisible;
/**
- * The PageErrorDisplay component that will display page state validation errors on this page
+ * The PageErrorDisplay component that will display page state validation
+ * errors on this page
*/
private Component m_errorDisplay;
/**
@@ -165,39 +180,41 @@ public class Page extends SimpleComponent implements Container {
*/
private boolean m_finished = false;
/**
- * indicates whether pageState.stateAsURL() should export the entire state for this page, or
- * whether it should only export the control event as a URL and use the HttpSession for the rest
- * of the page state.
+ * indicates whether pageState.stateAsURL() should export the entire state
+ * for this page, or whether it should only export the control event as a
+ * URL and use the HttpSession for the rest of the page state.
*/
private boolean m_useHttpSession = false;
/**
- * Returns true if this page should export state through the HttpSession instead of
- * the URL query string.
+ * Returns true if this page should export state through the
+ * HttpSession instead of the URL query string.
* true, then PageState.stateAsURL() will only export the control
- * event as a URL query string. If this returns false, then stateAsURL() will
- * export the entire page state.
+ * If this returns true, then PageState.stateAsURL() will only
+ * export the control event as a URL query string. If this returns
+ * false, then stateAsURL() will export the entire page state.
*
* @see PageState#stateAsURL
*
- * @return true if this page should export state through the HttpSession;
- * false if it should export using the URL query string.
+ * @return true if this page should export state through the
+ * HttpSession; false if it should export using the URL
+ * query string.
*/
public boolean isUsingHttpSession() {
return m_useHttpSession;
}
/**
- * Indicates to this page whether it should export its entire state to subsequent requests
- * through the URL query string, or if it should use the HttpSession instead and only use the
- * URL query string for the control event.
+ * Indicates to this page whether it should export its entire state to
+ * subsequent requests through the URL query string, or if it should use the
+ * HttpSession instead and only use the URL query string for the control
+ * event.
*
* @see PageState#stateAsURL
*
- * @param b true if PageState.stateAsURL() will export only the control event as a
- * URL query string. false if stateAsURL() will export the entire page
- * state.
+ * @param b true if PageState.stateAsURL() will export only the
+ * control event as a URL query string. false if
+ * stateAsURL() will export the entire page state.
*/
public void setUsingHttpSession(boolean b) {
m_useHttpSession = b;
@@ -273,7 +290,8 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Creates an empty page with the specified title and implicit BoxPanel container.
+ * Creates an empty page with the specified title and implicit BoxPanel
+ * container.
*
* @param title title for this page
*/
@@ -284,7 +302,8 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Creates an empty page with the specified title and implicit BoxPanel container.
+ * Creates an empty page with the specified title and implicit BoxPanel
+ * container.
*
* @param title title for this page
*/
@@ -304,12 +323,13 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Adds a component with the specified layout constraints to this container. Layout constraints
- * are defined in each layout container as static ints. To specify multiple constraints, use
- * bitwise OR.
+ * Adds a component with the specified layout constraints to this container.
+ * Layout constraints are defined in each layout container as static ints.
+ * To specify multiple constraints, use bitwise OR.
*
* @param c component to add to this container
- * @param constraints layout constraints (a bitwise OR of static ints in the particular layout)
+ * @param constraints layout constraints (a bitwise OR of static ints in the
+ * particular layout)
*/
@Override
public void add(Component c, int constraints) {
@@ -317,18 +337,20 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Returns true if this list contains the specified element. More formally, returns
- * true if and only if this list contains at least one element e such that (o==null
- * ? e==null : o.equals(e)).
+ * Returns true if this list contains the specified element.
+ * More formally, returns true if and only if this list
+ * contains at least one element e such that (o==null ? e==null :
+ * o.equals(e)).
* true only if the component has been directly added to this
- * container. If this container contains another container that contains this component, this
- * method returns false.
+ * This method returns true only if the component has been
+ * directly added to this container. If this container contains another
+ * container that contains this component, this method returns
+ * false.
*
* @param o element whose presence in this container is to be tested
*
- * @return true if this Container contains the specified component directly;
- * false otherwise.
+ * @return true if this Container contains the specified
+ * component directly; false otherwise.
*/
@Override
public boolean contains(Object o) {
@@ -336,9 +358,10 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Returns the component at the specified position. Each call to the add method increments the
- * index. Since the user has no control over the index of added components (other than counting
- * each call to add), this method should be used in conjunction with indexOf.
+ * Returns the component at the specified position. Each call to the add
+ * method increments the index. Since the user has no control over the index
+ * of added components (other than counting each call to add), this method
+ * should be used in conjunction with indexOf.
*
* @param index the index of the item to be retrieved from this Container
*
@@ -354,12 +377,11 @@ public class Page extends SimpleComponent implements Container {
*
* @param c component to search for
*
- * @return the index in this list of the first occurrence of the specified element, or -1 if
- * this list does not contain this element.
+ * @return the index in this list of the first occurrence of the specified
+ * element, or -1 if this list does not contain this element.
*
* @pre c != null
- * @post contains(c) implies (return >= 0) && (return < size())
- * @pos
+ * @post contains(c) implies (return >= 0) && (return < size()) @pos
* t !contains(c) implies return == -1
*/
@Override
@@ -370,8 +392,8 @@ public class Page extends SimpleComponent implements Container {
/**
* Returns true if the container contains no components.
*
- * @return true if this container contains no components; false
- * otherwise.
+ * @return true if this container contains no components;
+ * false otherwise.
*/
@Override
public boolean isEmpty() {
@@ -379,8 +401,9 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Returns the number of elements in this container. This does not recursively count the
- * components that are indirectly contained in this container.
+ * Returns the number of elements in this container. This does not
+ * recursively count the components that are indirectly contained in this
+ * container.
*
* @return the number of components directly in this container.
*/
@@ -395,7 +418,8 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Returns the panel that the Page uses for rendering its components.
+ * Returns the panel that the Page uses for rendering its
+ * components.
*
* @return the panel.
*/
@@ -404,8 +428,9 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Set the Container used for rendering components on this page. Caution should be used with
- * this function, as the existing container is simply overwritten.
+ * Set the Container used for rendering components on this page. Caution
+ * should be used with this function, as the existing container is simply
+ * overwritten.
*
* @param c
*
@@ -456,15 +481,16 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Sets the {@link Component} that will display the validation errors in the current
- * {@link PageState}. Any validation error in the PageState will cause the
- * Page to completely ignore all other components and render only the error display
- * component.
+ * Sets the {@link Component} that will display the validation errors in the
+ * current {@link PageState}. Any validation error in the
+ * PageState will cause the Page to completely
+ * ignore all other components and render only the error display component.
* PageState
+ * @param c the component that will display the validation errors in the
+ * current PageState
*/
public final void setErrorDisplay(Component c) {
Assert.isUnlocked(this);
@@ -472,32 +498,35 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Gets the {@link Component} that will display the validation errors in the current
- * {@link PageState}. Any validation error in the PageState will cause the
- * Page to completely ignore all other components and render only the error display
- * component.
+ * Gets the {@link Component} that will display the validation errors in the
+ * current {@link PageState}. Any validation error in the
+ * PageState will cause the Page to completely
+ * ignore all other components and render only the error display component.
* PageState.
+ * @return the component that will display the validation errors in the
+ * current PageState.
*/
public final Component getErrorDisplay() {
return m_errorDisplay;
}
/**
- * Adds a client-side stylesheet that should be used in HTML output. Arbitrarily many
- * client-side stylesheets can be added with this method. To use a CSS stylesheet, call
- * something like setStyleSheet("style.css", "text/css").
+ * Adds a client-side stylesheet that should be used in HTML output.
+ * Arbitrarily many client-side stylesheets can be added with this method.
+ * To use a CSS stylesheet, call something like
+ * setStyleSheet("style.css", "text/css").
*
*
@@ -543,7 +573,8 @@ public class Page extends SimpleComponent implements Container {
* ... page content gnerated by children ...
* </bebop:page> The content of the <title>
* element can be set by calling {@link #setTitle setTitle}. The
- * <stylesheet> element will only be present if a stylesheet has been set with {@link
+ * <stylesheet> element will only be present if a stylesheet
+ * has been set with {@link
* #setStyleSheet setStyleSheet}.
*
* @param ps the page state for the current page
@@ -573,8 +604,9 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Constructs a DOM or JDOM tree with all components on the page. The tree represents the page
- * that results from the {@link javax.servlet.http.HttpServletRequest} kept in the
+ * Constructs a DOM or JDOM tree with all components on the page. The tree
+ * represents the page that results from the
+ * {@link javax.servlet.http.HttpServletRequest} kept in the
* state.
*
* @param state the page state produced by {@link #process}
@@ -603,10 +635,33 @@ public class Page extends SimpleComponent implements Container {
if (Kernel.getConfig().isDebugEnabled() && debugStructure(state.
getRequest())) {
- Element structure = page.newChildElement("bebop:structure", BEBOP_XML_NS);
+ Element structure = page.newChildElement("bebop:structure",
+ BEBOP_XML_NS);
showStructure(state, structure);
}
+
+ final HttpServletRequest request = state.getRequest();
+ final HttpServletResponse response = state.getResponse();
+ if (response.isCommitted()) {
+ s_log.warn("Response already committed!!!");
+ }
+ final WebConfig webConfig = WebConfig.getInstanceOf();
+ if (webConfig.getVaryHeaders() != null
+ && !webConfig.getVaryHeaders().isEmpty()) {
+ response.addHeader("Vary", webConfig.getVaryHeaders());
+ }
+ response.addHeader("Content-Language",
+ GlobalizationHelper.getNegotiatedLocale().toString());
+ final HttpSession session = request.getSession();
+ if (session != null && session.getAttribute(
+ GlobalizationHelper.LANG_PARAM) != null) {
+
+ response.addCookie(new Cookie(
+ GlobalizationHelper.LANG_PARAM,
+ (String) session.getAttribute(
+ GlobalizationHelper.LANG_PARAM)));
+ }
}
private static boolean debugStructure(HttpServletRequest req) {
@@ -623,11 +678,11 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Creates a PageState object and processes it by calling the respond method on the selected
- * component. Processes a request by notifying the component from which the process originated
- * and {@link #fireActionEvent
- * broadcasts} an {@link ActionEvent} to all the listeners that registered with
- * {@link #addActionListener addActionListener}.
+ * Creates a PageState object and processes it by calling the respond method
+ * on the selected component. Processes a request by notifying the component
+ * from which the process originated and {@link #fireActionEvent
+ * broadcasts} an {@link ActionEvent} to all the listeners that registered
+ * with {@link #addActionListener addActionListener}.
*
* @see #generateXML(PageState,Document) generateXML
*
@@ -656,8 +711,8 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Processes the supplied PageState object according to this PageModel. Calls the respond method
- * on the selected Bebop component.
+ * Processes the supplied PageState object according to this PageModel.
+ * Calls the respond method on the selected Bebop component.
*/
public void process(PageState state) throws ServletException {
Assert.isLocked(this);
@@ -689,11 +744,13 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Builds a DOM Document from the current request state by doing a depth-first tree walk on the
- * current set of components in this Page, calling generateXML on each. Does NOT do the
- * rendering. If the HTTP response has already been committed, does not build the XML document.
+ * Builds a DOM Document from the current request state by doing a
+ * depth-first tree walk on the current set of components in this Page,
+ * calling generateXML on each. Does NOT do the rendering. If the HTTP
+ * response has already been committed, does not build the XML document.
*
- * @return a DOM ready for rendering, or null if the response has already been committed.
+ * @return a DOM ready for rendering, or null if the response has already
+ * been committed.
*
* @post res.isCommitted() == (return == null)
*/
@@ -725,8 +782,8 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Finishes building the page. The tree of components is traversed and each component is told to
- * add its state parameters to the page's state model.
+ * Finishes building the page. The tree of components is traversed and each
+ * component is told to add its state parameters to the page's state model.
*
* @pre ! isLocked()
*/
@@ -760,7 +817,8 @@ public class Page extends SimpleComponent implements Container {
* Locks the page and all its components against further modifications.
*
*
true if the component is visible by default; false
- * otherwise.
+ * @return true if the component is visible by default;
+ * false otherwise.
*
* @see #setVisibleDefault setVisibleDefault
* @see Component#setVisible Component.setVisible
@@ -1096,16 +1158,18 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Sets whether the specified component is visible by default. The default visibility is used
- * when a page is displayed for the first time and on subsequent requests until the visibility
- * of a component is changed explicitly with {@link Component#setVisible
+ * Sets whether the specified component is visible by default. The default
+ * visibility is used when a page is displayed for the first time and on
+ * subsequent requests until the visibility of a component is changed
+ * explicitly with {@link Component#setVisible
* Component.setVisible}.
*
*
* When a component is first added to a page, it is visible.
*
* @param c a component whose visibility is to be set
- * @param v true if the component is visible; false otherwise.
+ * @param v true if the component is visible;
+ * false otherwise.
*
* @see Component#setVisible Component.setVisible
* @see Component#register Component.register
@@ -1123,7 +1187,8 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * The global name of the parameter name in the component c.
+ * The global name of the parameter name in the component
+ * c.
*/
public String parameterName(Component c, String name) {
if (c == null || !stateContains(c)) {
@@ -1161,7 +1226,8 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * Return the prefix that is prepended to each component's state parameters to keep them unique.
+ * Return the prefix that is prepended to each component's state parameters
+ * to keep them unique.
*/
private final String componentPrefix(Component c) {
if (c == null) {
@@ -1219,10 +1285,12 @@ public class Page extends SimpleComponent implements Container {
selected.setText(sel);
// Control event
- Element eventName = state.newChildElement("bebop:eventName", BEBOP_XML_NS);
+ Element eventName = state.newChildElement("bebop:eventName",
+ BEBOP_XML_NS);
eventName.addAttribute(NAME, m_controlEvent.getName());
eventName.setText(req.getParameter(m_controlEvent.getName()));
- Element eventValue = state.newChildElement("bebop:eventValue", BEBOP_XML_NS);
+ Element eventValue = state.newChildElement("bebop:eventValue",
+ BEBOP_XML_NS);
eventValue.addAttribute(NAME, m_controlValue.getName());
eventValue.setText(req.getParameter(m_controlValue.getName()));
@@ -1231,7 +1299,8 @@ public class Page extends SimpleComponent implements Container {
for (Iterator ii = getStateModel().getParameters(); ii.hasNext();) {
ParameterModel p = (ParameterModel) ii.next();
if (!p.getName().startsWith(COMPONENT_PREFIX)) {
- Element param = globalState.newChildElement("bebop:param", BEBOP_XML_NS);
+ Element param = globalState.newChildElement("bebop:param",
+ BEBOP_XML_NS);
param.addAttribute(NAME, p.getName());
param.setText(String.valueOf(s.getValue(p)));
}
@@ -1260,11 +1329,13 @@ public class Page extends SimpleComponent implements Container {
continue;
}
- Element param = parent.newChildElement("bebop:param", BEBOP_XML_NS);
+ Element param = parent.newChildElement("bebop:param",
+ BEBOP_XML_NS);
param.addAttribute(NAME, unmangle(p.getName()));
param.addAttribute("defaultValue",
String.valueOf(req.getParameter(p.getName())));
- param.addAttribute("currentValue", String.valueOf(s.getValue(p)));
+ param
+ .addAttribute("currentValue", String.valueOf(s.getValue(p)));
}
}
for (Iterator i = c.children(); i.hasNext();) {
@@ -1282,9 +1353,9 @@ public class Page extends SimpleComponent implements Container {
}
/**
- * return a string that represents an ordered list of component ids used on the page. For
- * situations where only the components present is of importance, this may be used by
- * implementations of hashCode & equals
+ * return a string that represents an ordered list of component ids used on
+ * the page. For situations where only the components present is of
+ * importance, this may be used by implementations of hashCode & equals
*
* @return
*/
@@ -1304,7 +1375,8 @@ public class Page extends SimpleComponent implements Container {
String componentId = (String) it.next();
hashString.append(componentId);
}
- s_log.debug("Time to create hashCode for page: " + (new Date().getTime() - start.
+ s_log.debug("Time to create hashCode for page: " + (new Date().getTime()
+ - start.
getTime()));
return hashString.toString();
diff --git a/ccm-core/src/com/arsdigita/globalization/GlobalizationHelper.java b/ccm-core/src/com/arsdigita/globalization/GlobalizationHelper.java
index fe4766d94..a6c1162d9 100644
--- a/ccm-core/src/com/arsdigita/globalization/GlobalizationHelper.java
+++ b/ccm-core/src/com/arsdigita/globalization/GlobalizationHelper.java
@@ -21,7 +21,7 @@ import javax.servlet.http.HttpSession;
public class GlobalizationHelper {
public static final String LANG_INDEPENDENT = Kernel.getConfig().getLanguagesIndependentCode();
- private static final String LANG_PARAM = "lang";
+ public static final String LANG_PARAM = "lang";
// Don't instantiate
private GlobalizationHelper() {
diff --git a/ccm-core/src/com/arsdigita/web/CCMDispatcherServlet.java b/ccm-core/src/com/arsdigita/web/CCMDispatcherServlet.java
index 8d3e393f6..e18f52253 100755
--- a/ccm-core/src/com/arsdigita/web/CCMDispatcherServlet.java
+++ b/ccm-core/src/com/arsdigita/web/CCMDispatcherServlet.java
@@ -23,6 +23,7 @@ import com.arsdigita.developersupport.DeveloperSupport;
import com.arsdigita.dispatcher.DispatcherHelper;
import com.arsdigita.dispatcher.RequestEvent;
import com.arsdigita.domain.DataObjectNotFoundException;
+import com.arsdigita.globalization.GlobalizationHelper;
import com.arsdigita.persistence.SessionManager;
import com.arsdigita.persistence.TransactionContext;
import com.arsdigita.profiler.Profiler;
@@ -40,23 +41,26 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpSession;
// NOTE
// Combines and replaces the previous classes DispatcherServlet and BaseDispatcher
// Most of their code and their separation are abundant as old style applications
// are no longer supported.
-
-
/**
- *
The CCM main dispatcher. This servlet serves as the main servlet / main - * entry point (mapped to "/someprefix/*") for requests to any CCM webapp.
+ *+ * The CCM main dispatcher. This servlet serves as the main servlet / main entry + * point (mapped to "/someprefix/*") for requests to any CCM webapp.
* - *Upon finding an {@link com.arsdigita.web.Application application} at the + *
+ * Upon finding an {@link com.arsdigita.web.Application application} at the * requested URL, this class sets a request attribute storing the ID of the - * application and forwards to the servlet associated with that application. - * If instead no application is found, a 404 response is generated.
+ * application and forwards to the servlet associated with that application. If + * instead no application is found, a 404 response is generated. * - *This servlet has to be deployed using web.xml entries like these:
+ *+ * This servlet has to be deployed using web.xml entries like these:
* ** - ** <servlet> @@ -70,7 +74,8 @@ import org.apache.log4j.Logger; * </servlet-mapping> *
It's important to also edit the com.arsdigita.web.WebConfig m_servlet + *
+ * It's important to also edit the com.arsdigita.web.WebConfig m_servlet * parameter to reflect where you've put your dispatcher.
* ** * @see com.arsdigita.web.BaseApplicationServlet - * @author Justin Ross <jross@redhat.com> - * @author Peter Boy <Peter Boy> + * @author Justin Ross + * <jross@redhat.com> + * @author Peter Boy <Peter + * Boy> * @version $Id: DispatcherServlet.java 738 2005-09-01 12:36:52Z sskracic $ */ public class CCMDispatcherServlet extends BaseServlet { - /** 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.CCMDispatcherServlet=DEBUG by uncommenting - * or adding the line. */ + /** + * 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.CCMDispatcherServlet=DEBUG by uncommenting or + * adding the line. + */ private static final Logger s_log = Logger.getLogger( - CCMDispatcherServlet.class); + CCMDispatcherServlet.class); - static final String DISPATCHED_ATTRIBUTE = - CCMDispatcherServlet.class.getName() + ".dispatched"; + static final String DISPATCHED_ATTRIBUTE = CCMDispatcherServlet.class + .getName() + ".dispatched"; - /** Instance of the private Cache class */ + /** + * Instance of the private Cache class + */ private final static Cache s_cache = new Cache(); - - /** String containing the web context path portion of the WEB application - * where this CCMDispatcherServlet is executed. (I.e. where the WEB-INF - * directory containing the web.xml configuring this CCMDispatcherServlet - * is located in the servlet container webapps directory. - * */ + + /** + * String containing the web context path portion of the WEB application + * where this CCMDispatcherServlet is executed. (I.e. where the WEB-INF + * directory containing the web.xml configuring this CCMDispatcherServlet is + * located in the servlet container webapps directory. + * + */ private static String s_contextPath; public final boolean isApplicationInCache(String path) { return s_cache.isCached(path); } - /** - * Servlet initializer uses the extension point of parent class. - * @throws ServletException + * Servlet initializer uses the extension point of parent class. + * + * @throws ServletException */ @Override public void doInit() throws ServletException { - + ServletContext servletContext = getServletContext(); s_contextPath = servletContext.getContextPath(); // For backwords compatibility reasons register the web application // context of the Core (root) application als "/" - // Web.registerServletContext("/", - // servletContext); - + // Web.registerServletContext("/", + // servletContext); + } /** * Extends the standard service() method of the parent BaseServlet class. * Looks up and identifies the web application addressed in the url and - * forwards to that application's ApplicationServlet. - * (new style legacy free) - * - * @param sreq - * @param sresp + * forwards to that application's ApplicationServlet. (new style legacy + * free) + * + * @param servletRequest + * @param servletResponse + * * @throws ServletException - * @throws IOException + * @throws IOException */ - @Override - protected void doService(final HttpServletRequest sreq, - final HttpServletResponse sresp) - throws ServletException, IOException { - - DeveloperSupport.requestStart(new RequestEvent(sreq, sresp, + @Override + protected void doService(final HttpServletRequest servletRequest, + final HttpServletResponse servletResponse) + throws ServletException, IOException { + + DeveloperSupport.requestStart(new RequestEvent(servletRequest, + servletResponse, null, true, false)); if (s_log.isDebugEnabled()) { - s_log.debug("Dispatching request " + sreq.getRequestURI() + " [" + - sreq.getContextPath() + "," + - sreq.getServletPath() + "," + - sreq.getPathInfo() + "," + - sreq.getQueryString() + "]"); + s_log.debug("Dispatching request " + servletRequest.getRequestURI() + + " [" + + servletRequest.getContextPath() + "," + + servletRequest + .getServletPath() + + "," + servletRequest.getPathInfo() + "," + + servletRequest + .getQueryString() + + "]"); } DeveloperSupport.startStage("CCMDispatcherServlet.doService"); - - final String path = sreq.getPathInfo(); + final String path = servletRequest.getPathInfo(); if (requiresTrailingSlash(path)) { - s_log.debug("The request URI needs a trailing slash; " + - "redirecting"); + s_log.debug("The request URI needs a trailing slash; " + + "redirecting"); - final String prefix = DispatcherHelper.getDispatcherPrefix(sreq); - String uri = sreq.getRequestURI(); + final String prefix = DispatcherHelper.getDispatcherPrefix( + servletRequest); + String uri = servletRequest.getRequestURI(); if (prefix != null && prefix.trim().length() > 0) { uri = prefix + uri; } - final String query = sreq.getQueryString(); + final String query = servletRequest.getQueryString(); String url = null; if (query == null) { - sresp.sendRedirect(sresp.encodeRedirectURL(uri + "/")); + servletResponse.sendRedirect(servletResponse.encodeRedirectURL( + uri + "/")); } else { - sresp.sendRedirect - (sresp.encodeRedirectURL(uri + "/?" + query)); + servletResponse.sendRedirect(servletResponse.encodeRedirectURL( + uri + "/?" + query)); } // return true; @@ -186,41 +206,64 @@ public class CCMDispatcherServlet extends BaseServlet { Assert.exists(path, String.class); - s_log.debug("Storing the path elements of the current request as " + - "the original path elements"); + s_log.debug("Storing the path elements of the current request as " + + "the original path elements"); - sreq.setAttribute(BaseServlet.REQUEST_URL_ATTRIBUTE, new URL(sreq)); + servletRequest.setAttribute(BaseServlet.REQUEST_URL_ATTRIBUTE, + new URL(servletRequest)); if (s_log.isDebugEnabled()) { s_log.debug("Using path '" + path + "' to lookup application"); } - DeveloperSupport.startStage("CCMDispatcherServlet.lookupApplicationSpec"); + DeveloperSupport.startStage( + "CCMDispatcherServlet.lookupApplicationSpec"); final ApplicationSpec spec = lookupApplicationSpec(path); - DeveloperSupport.endStage("CCMDispatcherServlet.lookupApplicationSpec"); + DeveloperSupport.endStage( + "CCMDispatcherServlet.lookupApplicationSpec"); if (spec == null) { s_log.debug("No application was found; doing nothing"); // return false; // we have to create a 404 page here! - String requestUri = sreq.getRequestURI(); // same as ctx.getRemainingURLPart() - sresp.sendError(404, requestUri + " not found on this server."); + String requestUri = servletRequest.getRequestURI(); // same as ctx.getRemainingURLPart() + servletResponse.sendError(404, requestUri + + " not found on this server."); } else { if (s_log.isDebugEnabled()) { - s_log.debug("Found application " + spec.getAppID() + "; " + - "dispatching to its servlet"); + s_log.debug("Found application " + spec.getAppID() + "; " + + "dispatching to its servlet"); } - sreq.setAttribute - (BaseApplicationServlet.APPLICATION_ID_ATTRIBUTE, - spec.getAppID()); - sreq.setAttribute(DISPATCHED_ATTRIBUTE, Boolean.TRUE); + servletRequest.setAttribute( + BaseApplicationServlet.APPLICATION_ID_ATTRIBUTE, + spec.getAppID()); + servletRequest.setAttribute(DISPATCHED_ATTRIBUTE, Boolean.TRUE); Profiler.startOp("APP"); // +spec.getAppID() XXX get app name? - forward(spec.getTypeContextPath(), spec.target(path), sreq, sresp); + +// final WebConfig webConfig = WebConfig.getInstanceOf(); +// if (webConfig.getVaryHeaders() != null +// && !webConfig.getVaryHeaders().isEmpty()) { +// servletResponse +// .addHeader("Vary", webConfig.getVaryHeaders()); +// } +// final HttpSession session = servletRequest.getSession(); +// if (session != null && session.getAttribute( +// GlobalizationHelper.LANG_PARAM) != null) { +// +// servletResponse.addCookie(new Cookie( +// GlobalizationHelper.LANG_PARAM, +// (String) session.getAttribute( +// GlobalizationHelper.LANG_PARAM))); +// } + forward(spec.getTypeContextPath(), + spec.target(path), + servletRequest, + servletResponse); Profiler.stopOp("APP"); // +spec.getAppID() - // return true; + } } @@ -229,11 +272,11 @@ public class CCMDispatcherServlet extends BaseServlet { } - /** - * + * * @param path - * @return + * + * @return */ private boolean requiresTrailingSlash(final String path) { if (s_log.isDebugEnabled()) { @@ -241,8 +284,8 @@ public class CCMDispatcherServlet extends BaseServlet { } if (path == null) { - s_log.debug("The path is null; the request needs a trailing " + - "slash"); + s_log.debug("The path is null; the request needs a trailing " + + "slash"); return true; } @@ -252,32 +295,33 @@ public class CCMDispatcherServlet extends BaseServlet { } if (path.lastIndexOf(".") < path.lastIndexOf("/")) { - s_log.debug("The last fragment of the path has no '.', so we " + - "assume a directory was requested; a trailing " + - "slash is required"); + s_log.debug("The last fragment of the path has no '.', so we " + + "assume a directory was requested; a trailing " + + "slash is required"); return true; } else { - s_log.debug("The last fragment of the path appears to be a file " + - "name; no trailing slash is needed"); + s_log.debug("The last fragment of the path appears to be a file " + + "name; no trailing slash is needed"); return false; } } /** - * + * * @param contextPath * @param target * @param sreq * @param sresp + * * @throws ServletException - * @throws IOException + * @throws IOException */ private void forward(String contextPath, final String target, final HttpServletRequest sreq, final HttpServletResponse sresp) - throws ServletException, IOException { - + throws ServletException, IOException { + if (s_log.isDebugEnabled()) { s_log.debug("Forwarding by path to target '" + target + "'"); } @@ -291,11 +335,11 @@ public class CCMDispatcherServlet extends BaseServlet { // XXX We should pass servlet context down // final ServletContext context = Web.getServletContext(contextPath); final ServletContext context = Web.getServletContext() - .getContext(contextPath); - + .getContext(contextPath); + if (s_log.isDebugEnabled()) { - s_log.debug("From context " + Web.getServletContext() + - " to context " + context); + s_log.debug("From context " + Web.getServletContext() + + " to context " + context); } final RequestDispatcher rd = context.getRequestDispatcher(target); @@ -306,19 +350,20 @@ public class CCMDispatcherServlet extends BaseServlet { } /** - * + * * @param rd * @param sreq * @param sresp + * * @throws ServletException - * @throws IOException + * @throws IOException */ final void forward(final RequestDispatcher rd, HttpServletRequest sreq, final HttpServletResponse sresp) - throws ServletException, IOException { - s_log.debug("Checking if this request needs to be forwarded or " + - "included " + sreq); + throws ServletException, IOException { + s_log.debug("Checking if this request needs to be forwarded or " + + "included " + sreq); // Just in case sreq is wrapped in one of our own classes (spec. // MultipartHttpServletRequest), return unwrapped request, otherwise @@ -326,78 +371,98 @@ public class CCMDispatcherServlet extends BaseServlet { sreq = DispatcherHelper.restoreOriginalRequest(sreq); if (sreq.getAttribute("javax.servlet.include.request_uri") == null) { - s_log.debug("The attribute javax.servlet.include.request_uri " + - "is not set; forwarding " + sreq); + s_log.debug("The attribute javax.servlet.include.request_uri " + + "is not set; forwarding " + sreq); rd.forward(sreq, sresp); } else { - s_log.debug("The attribute javax.servlet.include.request_uri " + - "is set; including " + sreq); + s_log.debug("The attribute javax.servlet.include.request_uri " + + "is set; including " + sreq); rd.include(sreq, sresp); } + +// if (sresp.isCommitted()) { +// s_log.warn("Response already committed!!!"); +// } +// final WebConfig webConfig = WebConfig.getInstanceOf(); +// if (webConfig.getVaryHeaders() != null +// && !webConfig.getVaryHeaders().isEmpty()) { +// sresp +// .addHeader("Vary", webConfig.getVaryHeaders()); +// } +// final HttpSession session = sreq.getSession(); +// if (session != null && session.getAttribute( +// GlobalizationHelper.LANG_PARAM) != null) { +// +// sresp.addCookie(new Cookie( +// GlobalizationHelper.LANG_PARAM, +// (String) session.getAttribute( +// GlobalizationHelper.LANG_PARAM))); +// } } /** - * + * * @param path - * @return + * + * @return */ private ApplicationSpec lookupApplicationSpec(final String path) { if (s_log.isDebugEnabled()) { - s_log.debug("*** Starting application lookup for path '" + - path + "' ***"); + s_log.debug("*** Starting application lookup for path '" + path + + "' ***"); } ApplicationSpec spec = s_cache.getAppSpec(path); - if ( spec == null ) { + if (spec == null) { s_log.debug("There's no application to be found"); } return spec; } /** - * + * */ static void scheduleRefresh() { s_cache.scheduleRefresh(); } - + public static String getContextPath() { return s_contextPath; } /** - * + * */ /* Nothing specifically to destroy here @Override protected void doDestroy() { } - */ - - - + */ /** * Private class. */ private static class ApplicationSpec { + private final BigDecimal m_id; private final String m_instanceURI; private final String m_typeURI; private final String m_typeContextPath; /** - * - * @param app + * + * @param app */ ApplicationSpec(Application app) { - if ( app == null ) { throw new NullPointerException("app"); } + if (app == null) { + throw new NullPointerException("app"); + } - m_id = app.getID(); - m_instanceURI = app.getPath(); - m_typeURI = app.getServletPath(); + m_id = app.getID(); + m_instanceURI = app.getPath(); + m_typeURI = app.getServletPath(); m_typeContextPath = app.getContextPath(); if (Assert.isEnabled()) { @@ -409,38 +474,42 @@ public class CCMDispatcherServlet extends BaseServlet { } /** - * - * @return + * + * @return */ - BigDecimal getAppID() { return m_id; } - + BigDecimal getAppID() { + return m_id; + } + /** * Provides the context the application is executing. Usually all CCM - * applications will now execute in the same webapp context. The + * applications will now execute in the same webapp context. The * app.getContextPath() return "" in this case where an application is * executing in no specific context but CCM's default. + * * @return The context path of the application's url, "" in case of * executing in the ROOT context. */ - String getTypeContextPath() { - if (m_typeContextPath.equals("") ) { + String getTypeContextPath() { + if (m_typeContextPath.equals("")) { // app is running in CCM's default context, determine the // actual one return Web.getWebappContextPath(); } else { - return m_typeContextPath; + return m_typeContextPath; } } /** - * + * * @param path - * @return + * + * @return */ String target(final String path) { if (s_log.isDebugEnabled()) { - s_log.debug("Building the target path from the request path '" + - path + "' and the spec " + this); + s_log.debug("Building the target path from the request path '" + + path + "' and the spec " + this); } final StringBuffer target = new StringBuffer(128); @@ -460,37 +529,45 @@ public class CCMDispatcherServlet extends BaseServlet { } /** - * + * * @param obj - * @return + * + * @return */ @Override public boolean equals(Object obj) { - if ( obj==null ) { return false; } + if (obj == null) { + return false; + } ApplicationSpec other = (ApplicationSpec) obj; - return m_id.equals(other.m_id) && - equal(m_instanceURI, other.m_instanceURI) && - equal(m_typeURI, other.m_typeURI) && - equal(m_typeContextPath, other.m_typeContextPath); + return m_id.equals(other.m_id) && equal(m_instanceURI, + other.m_instanceURI) + && equal(m_typeURI, other.m_typeURI) && equal( + m_typeContextPath, other.m_typeContextPath); } /** - * + * * @param s1 * @param s2 - * @return + * + * @return */ private boolean equal(String s1, String s2) { - if (s1==s2) { return true; } - if (s1==null) { return equal(s2, s1); } + if (s1 == s2) { + return true; + } + if (s1 == null) { + return equal(s2, s1); + } return s1.equals(s2); } /** - * - * @return + * + * @return */ @Override public int hashCode() { @@ -498,8 +575,8 @@ public class CCMDispatcherServlet extends BaseServlet { } /** - * - * @return + * + * @return */ @Override public String toString() { @@ -512,22 +589,22 @@ public class CCMDispatcherServlet extends BaseServlet { sb.append("typeContextPath=").append(m_typeContextPath); return sb.append("]").toString(); } + } - - /** * Private class Cache caches (path, AppSpec) mappings. */ private static class Cache extends PathMapCache { - /** */ private static final ThreadLocal s_handleHere = new ThreadLocal() { - @Override - protected Object initialValue() { - return Boolean.FALSE; - } - }; + + @Override + protected Object initialValue() { + return Boolean.FALSE; + } + + }; /** * Constructor, just delegates to Super class @@ -539,23 +616,28 @@ public class CCMDispatcherServlet extends BaseServlet { // implements the PathMapCache interface @Override public String normalize(String path) { - if ( path==null ) { throw new NullPointerException("path"); } - if ( !path.startsWith("/") ) { - throw new DataObjectNotFoundException - ("The URL path specified must begin with a '/'."); + if (path == null) { + throw new NullPointerException("path"); } - return path.endsWith("/") ? - path : path.substring(0, path.lastIndexOf('/') + 1); + if (!path.startsWith("/")) { + throw new DataObjectNotFoundException( + "The URL path specified must begin with a '/'."); + } + return path.endsWith("/") ? path : path.substring(0, path + .lastIndexOf('/') + + 1); } // implements the PathMapCache interface @Override public Object retrieve(String path) { - if ( "/".equals(path) ) { return null; } + if ("/".equals(path)) { + return null; + } - final TransactionContext context = - SessionManager.getSession().getTransactionContext(); - if ( !context.inTxn() ) { + final TransactionContext context = SessionManager.getSession() + .getTransactionContext(); + if (!context.inTxn()) { s_log.debug("Beginning transaction"); context.beginTxn(); s_handleHere.set(Boolean.TRUE); @@ -563,7 +645,7 @@ public class CCMDispatcherServlet extends BaseServlet { Application app = Application.retrieveApplicationForPath(path); - return app==null ? null : new ApplicationSpec(app); + return app == null ? null : new ApplicationSpec(app); } // implements the PathMapCache interface @@ -580,12 +662,14 @@ public class CCMDispatcherServlet extends BaseServlet { try { return (ApplicationSpec) super.get(path); } finally { - if ( s_handleHere.get() == Boolean.TRUE ) { + if (s_handleHere.get() == Boolean.TRUE) { s_handleHere.set(Boolean.FALSE); SessionManager.getSession().getTransactionContext(). commitTxn(); } } } - } + + } + } diff --git a/ccm-core/src/com/arsdigita/web/WebConfig.java b/ccm-core/src/com/arsdigita/web/WebConfig.java index 0c9bd32c3..37b17287a 100755 --- a/ccm-core/src/com/arsdigita/web/WebConfig.java +++ b/ccm-core/src/com/arsdigita/web/WebConfig.java @@ -39,8 +39,8 @@ import org.apache.log4j.Logger; /** * A record containing server-session scoped configuration properties. * - * Accessors of this class may return null. Developers should take - * care to trap null return values in their code. + * Accessors of this class may return null. Developers should take care to trap + * null return values in their code. * * @see com.arsdigita.web.Web * @author Justin Ross <jross@redhat.com> @@ -48,111 +48,146 @@ import org.apache.log4j.Logger; */ public final class WebConfig extends AbstractConfig { - /** Internal logger instance to faciliate debugging. Enable logging output - * by editing /WEB-INF/conf/log4j.properties int the runtime environment - * and set com.arsdigita.web.WebConfig=DEBUG by uncommenting it */ + /** + * Internal logger instance to faciliate debugging. Enable logging output by + * editing /WEB-INF/conf/log4j.properties int the runtime environment and + * set com.arsdigita.web.WebConfig=DEBUG by uncommenting it + */ private static final Logger s_log = Logger.getLogger(WebConfig.class); - - /** Private Object to hold one's own instance to return to users. */ - private static WebConfig s_config ; - - /** - * Returns the singleton configuration record for the content section - * environment. - * - * @return The@@ -84,101 +89,116 @@ import org.apache.log4j.Logger; *
CMSConfig record; it cannot be null
- */
+
+ /**
+ * Private Object to hold one's own instance to return to users.
+ */
+ private static WebConfig s_config;
+
+ /**
+ * Returns the singleton configuration record for the content section
+ * environment.
+ *
+ * @return The CMSConfig record; it cannot be null
+ */
public static synchronized WebConfig getInstanceOf() {
if (s_config == null) {
s_config = new WebConfig();
s_config.load();
}
return s_config;
- }
+ }
// /////////////////////////////////////////////////////////////////////////
// Configuration parameter section
// /////////////////////////////////////////////////////////////////////////
-
- /** Determines what HTTP scheme prefix is used by default to generate URLs
- * (either http od https) */
- private final Parameter m_scheme = new DefaultSchemeParameter
- ("waf.web.default_scheme",
- Parameter.REQUIRED, "http");
- /** Sets the name and port that users of a site will see in URLs generated
- * by CCM for the site. This is a required parameter during installation,
- * e.g. example.com:80 */
- private final Parameter m_server = new HttpHostParameter
- ("waf.web.server");
- /** Name and port that users of a site will see in secure URLs generated
- * by CCM for the site. As an example: example.com:443 */
- private final Parameter m_secureServer = new HttpHostParameter
- ("waf.web.secure_server",
- Parameter.OPTIONAL, null);
- /** The name of your website, for use in page footers for example. It's
- * not necessarily the URL but rather a title, e.g. "House of HTML".
- * If not specified set to the server's URL. */
- private final Parameter m_site= new StringParameter
- ("waf.web.site_name",
- Parameter.OPTIONAL, null) { @Override
- public final Object getDefaultValue() {
- final HttpHost host = getServer();
- if (host == null) {
- return null;
- } else {
- return host.toString();
- }
- }
- };
- /** Sets the name and port of the machine on which the CCM instance is
- * running. Used to fetch some resources by a local URL avoiding external
- * internet traffic (and delay). If not specified set to the servers's
- * name redirecting all traffic to external internet address. */
- private final Parameter m_host = new HttpHostParameter
- ("waf.web.host",
- Parameter.OPTIONAL, null) { @Override
- public final Object getDefaultValue() {
- return getServer();
- }
- };
+ /**
+ * Determines what HTTP scheme prefix is used by default to generate URLs
+ * (either http od https)
+ */
+ private final Parameter m_scheme = new DefaultSchemeParameter(
+ "waf.web.default_scheme",
+ Parameter.REQUIRED, "http");
+ /**
+ * Sets the name and port that users of a site will see in URLs generated by
+ * CCM for the site. This is a required parameter during installation, e.g.
+ * example.com:80
+ */
+ private final Parameter m_server = new HttpHostParameter("waf.web.server");
+ /**
+ * Name and port that users of a site will see in secure URLs generated by
+ * CCM for the site. As an example: example.com:443
+ */
+ private final Parameter m_secureServer = new HttpHostParameter(
+ "waf.web.secure_server",
+ Parameter.OPTIONAL, null);
+ /**
+ * The name of your website, for use in page footers for example. It's not
+ * necessarily the URL but rather a title, e.g. "House of HTML". If not
+ * specified set to the server's URL.
+ */
+ private final Parameter m_site = new StringParameter("waf.web.site_name",
+ Parameter.OPTIONAL,
+ null) {
- /** List of URLs which accessed by insecure (normal HTTP) connection
- * produce a redirect to a HTTPS equivalent. List is comma separated. */
- private final Parameter m_secureRequired = new StringArrayParameter
- ("waf.web.secure_required", Parameter.OPTIONAL, null);
- /** List of URLs which accessed by secure (HTTPS) connection produce a
- * redirect to a HTTP equivalent. List is comma separated. */
- private final Parameter m_secureSwitchBack = new StringArrayParameter
- ("waf.web.secure_switchback", Parameter.OPTIONAL, null);
+ @Override
+ public final Object getDefaultValue() {
+ final HttpHost host = getServer();
+ if (host == null) {
+ return null;
+ } else {
+ return host.toString();
+ }
+ }
- /** Dispatcher servlet path. It's the prefix to the main entry point for
- * any application request (CCMDispatcherServlet). By default /ccm */
- private final Parameter m_servlet = new StringParameter
- ("waf.web.dispatcher_servlet_path", Parameter.REQUIRED, "/ccm");
+ };
+ /**
+ * Sets the name and port of the machine on which the CCM instance is
+ * running. Used to fetch some resources by a local URL avoiding external
+ * internet traffic (and delay). If not specified set to the servers's name
+ * redirecting all traffic to external internet address.
+ */
+ private final Parameter m_host = new HttpHostParameter("waf.web.host",
+ Parameter.OPTIONAL,
+ null) {
- /** Specifies by name which implementation of ApplicationFileResolver is
- * used to dynamically resolve static files. By default
- * DefaultApplicationFileResolver() is used. */
- private final Parameter m_resolver = new SingletonParameter
- ("waf.web.application_file_resolver",
- Parameter.OPTIONAL,
- new DefaultApplicationFileResolver());
- private final Parameter m_default_cache_policy = new CachePolicyParameter
- ("waf.web.cache_policy",
- Parameter.OPTIONAL, null);
- private final Parameter m_deactivate_cache_host_notifications = new BooleanParameter
- ("waf.web.deactivate_cache_host_notifications",
- Parameter.OPTIONAL, Boolean.FALSE);
+ @Override
+ public final Object getDefaultValue() {
+ return getServer();
+ }
- private final Parameter m_dynamic_host_provider = new StringParameter
- ("waf.web.dynamic_host_provider",
- Parameter.OPTIONAL, "");
+ };
- /**
- * Constructor, but do NOT instantiate this class directly, use
- * getInstanceOf() instead. (Singelton pattern!)
- *
- */
+ /**
+ * List of URLs which accessed by insecure (normal HTTP) connection produce
+ * a redirect to a HTTPS equivalent. List is comma separated.
+ */
+ private final Parameter m_secureRequired = new StringArrayParameter(
+ "waf.web.secure_required", Parameter.OPTIONAL, null);
+ /**
+ * List of URLs which accessed by secure (HTTPS) connection produce a
+ * redirect to a HTTP equivalent. List is comma separated.
+ */
+ private final Parameter m_secureSwitchBack = new StringArrayParameter(
+ "waf.web.secure_switchback", Parameter.OPTIONAL, null);
+
+ /**
+ * Dispatcher servlet path. It's the prefix to the main entry point for any
+ * application request (CCMDispatcherServlet). By default /ccm
+ */
+ private final Parameter m_servlet = new StringParameter(
+ "waf.web.dispatcher_servlet_path", Parameter.REQUIRED, "/ccm");
+
+ /**
+ * Specifies by name which implementation of ApplicationFileResolver is used
+ * to dynamically resolve static files. By default
+ * DefaultApplicationFileResolver() is used.
+ */
+ private final Parameter m_resolver = new SingletonParameter(
+ "waf.web.application_file_resolver",
+ Parameter.OPTIONAL,
+ new DefaultApplicationFileResolver());
+ private final Parameter m_default_cache_policy = new CachePolicyParameter(
+ "waf.web.cache_policy",
+ Parameter.OPTIONAL, null);
+ private final Parameter m_deactivate_cache_host_notifications
+ = new BooleanParameter(
+ "waf.web.deactivate_cache_host_notifications",
+ Parameter.OPTIONAL, Boolean.FALSE);
+
+ private final Parameter m_dynamic_host_provider = new StringParameter(
+ "waf.web.dynamic_host_provider",
+ Parameter.OPTIONAL, "");
+
+ /**
+ * Specifies the values set as {@code vary} HTTP header in the response.
+ */
+ private final Parameter m_varyHeaders = new StringParameter(
+ "waf.web.vary_headers",
+ Parameter.OPTIONAL,
+ "accept-language");
+
+ /**
+ * Constructor, but do NOT instantiate this class directly, use
+ * getInstanceOf() instead. (Singleton pattern!)
+ *
+ */
public WebConfig() {
register(m_scheme);
@@ -167,6 +202,7 @@ public final class WebConfig extends AbstractConfig {
register(m_default_cache_policy);
register(m_deactivate_cache_host_notifications);
register(m_dynamic_host_provider);
+ register(m_varyHeaders);
loadInfo();
}
@@ -175,10 +211,11 @@ public final class WebConfig extends AbstractConfig {
return (String) get(m_scheme);
}
- /**
+ /**
* Provide the name and port that users of a site will see in URLs generated
- * by CCM for the site. (Value of parameter waf.web.server)
- * E.g. example.com:80
+ * by CCM for the site. (Value of parameter waf.web.server) E.g.
+ * example.com:80
+ *
* @return HttpHost object, contains public name & port of the server (site)
*/
public final HttpHost getServer() {
@@ -188,11 +225,11 @@ public final class WebConfig extends AbstractConfig {
public final HttpHost getSecureServer() {
return (HttpHost) get(m_secureServer);
}
-
+
public final boolean isSecureRequired(String uri) {
- String[] secured = (String[])get(m_secureRequired);
+ String[] secured = (String[]) get(m_secureRequired);
if (secured != null) {
- for (int i=0, n=secured.length; iuser for
- * per-user caching, world for globally enabled
- * caching, disable to prevent HTTP header caching, and
- * noneto always prevent caching in any case.
- * @return
+ * Gets the system default cache policy. This value is set via the
+ * com.arsdigita.web.cache_policy system property using one fo
+ * the following values: user for per-user caching,
+ * world for globally enabled caching, disable to
+ * prevent HTTP header caching, and noneto always prevent
+ * caching in any case.
+ *
+ * @return
*/
public final CachePolicy getCachePolicy() {
return (CachePolicy) get(m_default_cache_policy);
}
private static class DispatcherServletPathParameter
- extends StringParameter {
+ extends StringParameter {
+
DispatcherServletPathParameter(final String name) {
super(name);
}
@@ -284,14 +322,16 @@ public final class WebConfig extends AbstractConfig {
final String string = (String) value;
if (string.endsWith("/")) {
- final ParameterError error = new ParameterError
- (this, "The value must not end in a '/'");
+ final ParameterError error = new ParameterError(this,
+ "The value must not end in a '/'");
errors.add(error);
}
}
+
}
private static class DefaultSchemeParameter extends EnumerationParameter {
+
DefaultSchemeParameter(final String name,
final int multiplicity,
final Object defaalt) {
@@ -300,9 +340,11 @@ public final class WebConfig extends AbstractConfig {
put("http", "http");
put("https", "https");
}
+
}
private static class CachePolicyParameter extends EnumerationParameter {
+
CachePolicyParameter(final String name,
final int multiplicity,
final Object defaalt) {
@@ -313,6 +355,7 @@ public final class WebConfig extends AbstractConfig {
put("user", CachePolicy.USER);
put("world", CachePolicy.WORLD);
}
+
}
protected DynamicHostProvider dhProvider = null;
@@ -326,7 +369,9 @@ public final class WebConfig extends AbstractConfig {
Class klass = Class.forName(classname);
dhProvider = (DynamicHostProvider) klass.newInstance();
} catch (Exception e) {
- s_log.error("Could not instantiate DynamicHostProvider using classname : "+classname, e);
+ s_log.error(
+ "Could not instantiate DynamicHostProvider using classname : "
+ + classname, e);
}
}
dhProviderInited = true;
@@ -335,32 +380,29 @@ public final class WebConfig extends AbstractConfig {
}
public final boolean getDeactivateCacheHostNotifications() {
- return ((Boolean) get(m_deactivate_cache_host_notifications)).booleanValue();
+ return ((Boolean) get(m_deactivate_cache_host_notifications))
+ .booleanValue();
}
//
// Deprecated classes and methods
//
-
/**
- * @return
- * @deprecated Use getServer().getName() instead.
+ * @return @deprecated Use getServer().getName() instead.
*/
public final String getServerName() {
return getServer().getName();
}
/**
- * @return
- * @deprecated Use getServer().getPort() instead.
+ * @return @deprecated Use getServer().getPort() instead.
*/
public final int getServerPort() {
return getServer().getPort();
}
/**
- * @return
- * @deprecated Use
+ * @return @deprecated Use
* Host.retrieve(Web.getConfig().getHost()) instead.
*/
public final Host getCurrentHost() {
@@ -368,8 +410,7 @@ public final class WebConfig extends AbstractConfig {
}
/**
- * @return
- * @deprecated Use Host.retrieveAll() instead.
+ * @return @deprecated Use Host.retrieveAll() instead.
*/
public final Host[] getHosts() {
final List hosts = new ArrayList();
@@ -383,4 +424,13 @@ public final class WebConfig extends AbstractConfig {
return (Host[]) hosts.toArray(new Host[hosts.size()]);
}
+
+ public final String getVaryHeaders() {
+ return (String) get(m_varyHeaders);
+ }
+
+ public void setVaryHeaders(final String value) {
+ set(m_varyHeaders, value);
+ }
+
}
diff --git a/ccm-core/src/com/arsdigita/web/WebConfig_parameter.properties b/ccm-core/src/com/arsdigita/web/WebConfig_parameter.properties
index e5d683421..51c41d066 100755
--- a/ccm-core/src/com/arsdigita/web/WebConfig_parameter.properties
+++ b/ccm-core/src/com/arsdigita/web/WebConfig_parameter.properties
@@ -57,3 +57,8 @@ waf.web.dynamic_host_provider.title=Dynamic Host Provider
waf.web.dynamic_host_provider.purpose=Class name of the DynamicHostProvider to use
waf.web.dynamic_host_provider.example=com.arsdigita.web.ServerDynamicHostProvider
waf.web.dynamic_host_provider.format=[string]
+
+waf.web.vary_headers.title=Value of vary header
+waf.web.vary_headers.purpose=Value of the vary header in the HTTP response. Set the an empty string to disable.
+waf.web.vary_headers.example=accept-language
+waf.web.vary_headers.format=[string]
diff --git a/ccm-sci-bundle/src/com/arsdigita/bundle/AddVaryHeaderFilter.java b/ccm-sci-bundle/src/com/arsdigita/bundle/AddVaryHeaderFilter.java
index 7b630ce71..6deedf170 100644
--- a/ccm-sci-bundle/src/com/arsdigita/bundle/AddVaryHeaderFilter.java
+++ b/ccm-sci-bundle/src/com/arsdigita/bundle/AddVaryHeaderFilter.java
@@ -1,5 +1,8 @@
package com.arsdigita.bundle;
+import com.arsdigita.globalization.GlobalizationHelper;
+import com.arsdigita.web.WebConfig;
+
import java.io.IOException;
import javax.servlet.Filter;
@@ -8,7 +11,10 @@ import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
/**
*
@@ -31,7 +37,24 @@ public class AddVaryHeaderFilter implements Filter {
final HttpServletResponse response
= (HttpServletResponse) servletResponse;
- response.addHeader("Vary", "accept-language");
+ final WebConfig webConfig = WebConfig.getInstanceOf();
+ if (webConfig.getVaryHeaders() != null
+ && !webConfig.getVaryHeaders().isEmpty()) {
+ response
+ .addHeader("Vary", webConfig.getVaryHeaders());
+ }
+ final HttpSession session = ((HttpServletRequest) servletRequest)
+ .getSession();
+ if (session != null && session.getAttribute(
+ GlobalizationHelper.LANG_PARAM) != null) {
+
+ response.addCookie(new Cookie(
+ GlobalizationHelper.LANG_PARAM,
+ (String) session.getAttribute(
+ GlobalizationHelper.LANG_PARAM)));
+ }
+
+// response.addHeader("Vary", "accept-language");
filterChain.doFilter(servletRequest, servletResponse);
}