diff --git a/ccm-core/src/com/arsdigita/bebop/Page.java b/ccm-core/src/com/arsdigita/bebop/Page.java index eb7e26909..cd0cf9c44 100755 --- a/ccm-core/src/com/arsdigita/bebop/Page.java +++ b/ccm-core/src/com/arsdigita/bebop/Page.java @@ -27,12 +27,15 @@ import com.arsdigita.bebop.parameters.ParameterModel; import com.arsdigita.bebop.parameters.StringParameter; import com.arsdigita.bebop.util.Traversal; import com.arsdigita.developersupport.DeveloperSupport; +import com.arsdigita.globalization.GlobalizationHelper; import com.arsdigita.kernel.Kernel; import com.arsdigita.profiler.Profiler; import com.arsdigita.util.Assert; import com.arsdigita.util.SystemInformation; +import com.arsdigita.web.WebConfig; import com.arsdigita.xml.Document; import com.arsdigita.xml.Element; + import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; @@ -45,21 +48,27 @@ import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.ParserConfigurationException; + import org.apache.log4j.Logger; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpSession; + /** * The top-level container for all Bebop components and containers. * *
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; /** - * Returnssystem property - * using one fo the following values:trueif this page should export state through the HttpSession instead of - * the URL query string. + * Returnstrueif this page should export state through the + * HttpSession instead of the URL query string. *- * If this returns
true, then PageState.stateAsURL() will only export the control - * event as a URL query string. If this returnsfalse, then stateAsURL() will - * export the entire page state. + * If this returnstrue, 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 * - * @returntrueif this page should export state through the HttpSession; - *falseif it should export using the URL query string. + * @returntrueif this page should export state through the + * HttpSession;falseif 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 btrueif PageState.stateAsURL() will export only the control event as a - * URL query string.falseif stateAsURL() will export the entire page - * state. + * @param btrueif PageState.stateAsURL() will export only the + * control event as a URL query string.falseif + * 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 { } /** - * Returnstrueif this list contains the specified element. More formally, returns - *trueif and only if this list contains at least one element e such that (o==null - * ? e==null : o.equals(e)). + * Returnstrueif this list contains the specified element. + * More formally, returnstrueif and only if this list + * contains at least one element e such that (o==null ? e==null : + * o.equals(e)). *- * This method returns
trueonly if the component has been directly added to this - * container. If this container contains another container that contains this component, this - * method returnsfalse. + * This method returnstrueonly 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 * - * @returntrueif this Container contains the specified component directly; - *falseotherwise. + * @returntrueif this Container contains the specified + * component directly;falseotherwise. */ @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 { /** * Returnstrueif the container contains no components. * - * @returntrueif this container contains no components;false- * otherwise. + * @returntrueif this container contains no components; + *falseotherwise. */ @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 thePageuses for rendering its components. + * Returns the panel that thePageuses 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 thePageStatewill cause the - *Pageto 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 + *PageStatewill cause thePageto completely + * ignore all other components and render only the error display component. *- * 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 - *
PageState+ * @param c the component that will display the validation errors in the + * currentPageState*/ 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 thePageStatewill cause the - *Pageto 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 + *PageStatewill cause thePageto completely + * ignore all other components and render only the error display component. *- * 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 - *
PageState. + * @return the component that will display the validation errors in the + * currentPageState. */ 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 likesetStyleSheet("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"). * ** 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: *
@@ -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. * *- * Locking a page helps in finding mistakes that result from modifying a page's structure.
+ * Locking a page helps in finding mistakes that result from modifying a + * page's structure. */ @Override public void lock() { @@ -788,8 +846,8 @@ public class Page extends SimpleComponent implements Container { } /** - * Registers a listener that is notified whenever a request to this page is made, after the - * selected component has had a chance to respond. + * Registers a listener that is notified whenever a request to this page is + * made, after the selected component has had a chance to respond. * * @pre l != null * @pre ! isLocked() @@ -811,8 +869,8 @@ public class Page extends SimpleComponent implements Container { } /** - * Registers a listener that is notified whenever a request to this page is made, before the - * selected component has had a chance to respond. + * Registers a listener that is notified whenever a request to this page is + * made, before the selected component has had a chance to respond. * * @pre l != null * @pre ! isLocked() @@ -836,9 +894,9 @@ public class Page extends SimpleComponent implements Container { } /** - * Broadcasts an {@link ActionEvent} to all registered listeners. The source of the event is - * this page, and the state recorded in the event is the one resulting from processing the - * current request. + * Broadcasts an {@link ActionEvent} to all registered listeners. The source + * of the event is this page, and the state recorded in the event is the one + * resulting from processing the current request. * * @param the state for this event * @@ -863,9 +921,9 @@ public class Page extends SimpleComponent implements Container { } /** - * Broadcasts a {@link RequestEvent} to all registered listeners. The source of the event is - * this page, and the state recorded in the event is the one resulting from processing the - * current request. + * Broadcasts a {@link RequestEvent} to all registered listeners. The source + * of the event is this page, and the state recorded in the event is the one + * resulting from processing the current request. * * @param state the state for this event * @@ -890,9 +948,9 @@ public class Page extends SimpleComponent implements Container { } /** - * Export page generator information if set. The m_pageGenerator is a HashMap containing the - * information as key value. In general this should include generator name and generator - * version. + * Export page generator information if set. The m_pageGenerator is a + * HashMap containing the information as key value. In general this should + * include generator name and generator version. * * @param page parent element - should be bebeop:page * @@ -901,7 +959,8 @@ public class Page extends SimpleComponent implements Container { final protected void exportSystemInformation(Element page) { SystemInformation sysInfo = SystemInformation.getInstance(); if (!sysInfo.isEmpty()) { - Element gen = page.newChildElement("bebop:systemInformation", BEBOP_XML_NS); + Element gen = page.newChildElement("bebop:systemInformation", + BEBOP_XML_NS); Iterator> keyValues = sysInfo.iterator(); while (keyValues.hasNext()) { @@ -944,7 +1003,8 @@ public class Page extends SimpleComponent implements Container { if (!stateContains(c)) { if (c == null) { s_log.error("c is null"); - } /*else { + } + /*else { s_log.error("c: " + c.toString()); }*/ @@ -963,10 +1023,12 @@ public class Page extends SimpleComponent implements Container { } /** - * Registers a state parameter for a component. It is permissible to register the same state - * parameter several times, from the same or different components. The name of the parameter - * will be changed to ensure that it won't clash with any other component's parameter. If the - * parameter is added more than once, the name is only changed the first time it is added. + * Registers a state parameter for a component. It is permissible to + * register the same state parameter several times, from the same or + * different components. The name of the parameter will be changed to ensure + * that it won't clash with any other component's parameter. If the + * parameter is added more than once, the name is only changed the first + * time it is added. * * @param c the component to register the parameter for * @param p the state parameter to register @@ -986,8 +1048,9 @@ public class Page extends SimpleComponent implements Container { } if (!m_stateModel.containsFormParam(p)) { String name = parameterName(c, p.getName()); - s_log.debug(String.format("Setting name of parameter to add to '%s'", - name)); + s_log.debug(String + .format("Setting name of parameter to add to '%s'", + name)); p.setName(name); m_stateModel.addFormParam(p); @@ -1009,17 +1072,17 @@ public class Page extends SimpleComponent implements Container { } /** - * Gets the state index of a component. This is the number assigned to the component in the - * register traveral + * Gets the state index of a component. This is the number assigned to the + * component in the register traveral * * @param c the 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 - * t !contains(c) implies return == -1 + * @post contains(c) implies (return >= 0) && (return < size()) @pos t + * !contains(c) implies return == -1 */ public int stateIndex(Component c) { return m_components.indexOf(c); @@ -1046,8 +1109,7 @@ public class Page extends SimpleComponent implements Container { /** * Gets a page component by index. * - * @pre (i >= 0) && (i < size()) @pos - * t return != null + * @pre (i >= 0) && (i < size()) @pos t return != null */ public Component getComponent(int i) { return (Component) m_components.get(i); @@ -1083,8 +1145,8 @@ public class Page extends SimpleComponent implements Container { * * @param c a component contained in the page * - * @return trueif the component is visible by default;false- * otherwise. + * @returntrueif the component is visible by default; + *falseotherwise. * * @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
trueif the component is visible;falseotherwise. + * @param vtrueif the component is visible; + *falseotherwise. * * @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 parameternamein the componentc. + * The global name of the parameternamein 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; *CMSConfigrecord; 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 TheCMSConfigrecord; 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; icom.arsdigita.web.cache_policy userfor - * per-user caching,worldfor globally enabled - * caching,disableto 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_policysystem property using one fo + * the following values:userfor per-user caching, + *worldfor globally enabled caching,disableto + * prevent HTTP header caching, andnoneto 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 UsegetServer().getName()instead. + * @return @deprecated UsegetServer().getName()instead. */ public final String getServerName() { return getServer().getName(); } /** - * @return - * @deprecated UsegetServer().getPort()instead. + * @return @deprecated UsegetServer().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 UseHost.retrieveAll()instead. + * @return @deprecated UseHost.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); }