001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.framework;
011:
012: import java.io.*;
013: import java.util.*;
014: import org.mmbase.bridge.Node;
015: import org.mmbase.util.*;
016: import org.mmbase.util.xml.Instantiator;
017: import org.mmbase.util.functions.*;
018: import org.mmbase.util.logging.Logger;
019: import org.mmbase.util.logging.Logging;
020:
021: /**
022: * A framework displays and processes components.
023: *
024: * The Framework as UrlConverter must never return <code>null</code>, iow, it should
025: * <em>always</em> know how to do this.
026: *
027: *
028: * @author Johannes Verelst
029: * @author Pierre van Rooden
030: * @version $Id: Framework.java,v 1.47 2008/02/24 10:46:20 michiel Exp $
031: * @since MMBase-1.9
032: */
033: public abstract class Framework {
034:
035: private static final Logger log = Logging
036: .getLoggerInstance(Framework.class);
037:
038: /**
039: * Reference to the Framework singleton.
040: * @since MMBase-1.9
041: */
042: static Framework framework = null;
043:
044: public static final String XSD = "framework.xsd";
045: public static final String NAMESPACE = "http://www.mmbase.org/xmlns/framework";
046: static {
047: XMLEntityResolver.registerSystemID(NAMESPACE + ".xsd", XSD,
048: Framework.class);
049: }
050:
051: /**
052: * Return the framework, or null if there is no framework defined in mmbaseroot.xml
053: * @return the framework
054: */
055: public static Framework getInstance() {
056: if (framework == null) {
057: org.mmbase.util.ResourceWatcher frameworkWatcher = new org.mmbase.util.ResourceWatcher() {
058: public void onChange(String resourceName) {
059: try {
060: ComponentRepository.getInstance();
061: org.w3c.dom.Document fwConfiguration = getResourceLoader()
062: .getDocument(resourceName, true,
063: Framework.class);
064: if (fwConfiguration == null) {
065: framework = new org.mmbase.framework.basic.BasicFramework();
066: } else {
067: org.w3c.dom.Element el = fwConfiguration
068: .getDocumentElement();
069: try {
070: framework = (Framework) Instantiator
071: .getInstance(el, el);
072: } catch (NoSuchMethodError nsme) {
073: framework = (Framework) Instantiator
074: .getInstance(el);
075: }
076: }
077: org.w3c.dom.NodeList blockTypes = fwConfiguration
078: .getDocumentElement()
079: .getElementsByTagName("blocktype");
080: for (int i = 0; i < blockTypes.getLength(); i++) {
081: org.w3c.dom.Element element = (org.w3c.dom.Element) blockTypes
082: .item(i);
083: String classification = element
084: .getAttribute("name");
085: int weight = Integer.parseInt(element
086: .getAttribute("weight"));
087: for (Block.Type t : Block.Type
088: .getClassification(classification,
089: true)) {
090: t.setWeight(weight);
091: }
092: }
093: } catch (Exception e) {
094: log.error(e.getMessage(), e);
095: framework = new org.mmbase.framework.basic.BasicFramework();
096: }
097: }
098: };
099: frameworkWatcher.add("framework.xml");
100: frameworkWatcher.setDelay(10 * 1000); // check every 10 secs if config changed
101: frameworkWatcher.start();
102: frameworkWatcher.onChange();
103: }
104: return framework;
105: }
106:
107: /**
108: * CSS-id to be used on block
109: */
110: public final static String COMPONENT_ID_KEY = "componentId";
111:
112: /**
113: * CSS-class to be used on block. T
114: */
115: public final static String COMPONENT_CLASS_KEY = "componentClassName";
116:
117: /**
118: * A framework must be able to provide a node to the rendered blocks. This parameter could
119: * indicate _which_ node.
120: * @todo Not yet suported, so basic framework cannot yet support block which require a framework
121: * provided node.
122: */
123: public static final Parameter<Node> N = new Parameter<Node>("n",
124: Node.class);
125:
126: /**
127: * Return the name of the framework
128: */
129: public abstract String getName();
130:
131: /**
132: * Returns the block which is specified by framework parameters.
133: */
134: public abstract Block getBlock(Parameters frameworkParameters);
135:
136: public abstract Block getRenderingBlock(
137: Parameters frameworkParameters);
138:
139: /**
140: * Return a Parameters object that needs to be passed on to the getUrl() call.
141: *
142: * Many components will be implemented as servlets, so will not work if the framework does not
143: * at least include {@link Parameter.REQUEST} and {@link Parameter.RESPONSE}. So it is
144: * recommended that those parameters are supported by the framework.
145: *
146: *
147: *
148: * The MMBase taglib component tag will e.g. auto-fill those parameters. Other parameters can be
149: * added using 'mm:frameworkparameter'
150: *
151: * A framework may create a different or expanded list of parameters, but is responsible for filling them properly.
152: * If the framework does not use the MMBase taglib for rendering of components, it needs to provide it's own mechanism to
153: * fill the above parameters with default values (such as through a servlet or portlet).
154: */
155: public abstract Parameters createParameters();
156:
157: /**
158: * Render content (such as HTML or XML) using a Renderer obtained from a component's block.
159: * The framework decides on a (extra) class for the div which is to be rendered, which is put on
160: * the request as {@link #COMPONENT_CLASS_KEY}.
161: *
162: * @param renderer the Renderer used to produce the content. This parameter is obtained using {@link Block#getRenderer()}
163: * @param blockParameters The parameters specific for the call of this renderer's block
164: * @param frameworkParameters The parameters that are required by the framework, such as the 'request' and 'cloud' objects
165: * @param w The writer where the code generated by the renderer is to be written (such as the jspWriter)
166: * @param state the window state in which the content should be rendered
167: * @throws FrameworkException when the renderer failed to create content or could not write data to the writer
168: */
169: public abstract void render(Renderer renderer,
170: Parameters blockParameters, Parameters frameworkParameters,
171: Writer w, Renderer.WindowState state)
172: throws FrameworkException;
173:
174: /**
175: * Processes a block. This method can change or se state information and should be called prior to rendering a component's block.
176: * A process does not generate content.
177: *
178: * @param processor the Processor used to produce the content. This parameter is obtained using {@link Block#getProcessor()}
179: * @param blockParameters The parameters specific for the call of this renderer's block.
180: * @param frameworkParameters The parameters that are required by the framework, such as the 'request' and 'cloud' objects.
181: * @throws FrameworkException when the process failed to run
182: */
183: public abstract void process(Processor processor,
184: Parameters blockParameters, Parameters frameworkParameters)
185: throws FrameworkException;
186:
187: /**
188: * Return an MMBase Node for the user currently using the framework. It is recommended that this
189: * is implemented as is done in {@link BasicFramework}, so based on MMBase security only, and
190: * using a {@link Parameter.CLOUD} as a framework parameter. It can be implemented differently,
191: * if the framework chooses not to use MMBase security to distinguish between users.
192: */
193: public abstract Node getUserNode(Parameters frameworkParameters);;
194:
195: /**
196: * Return the builder name that is used to store users. This will return the name of the nodemanager that returns
197: * the nodes from the getUserNode() method.
198: * @TODO What if the framework wants to return virtual nodes?
199: * @throws UnsupportedOperationException
200: */
201: public abstract String getUserBuilder();
202:
203: /**
204: * @see #getSettingValue(Setting, Parameters)
205: * @see #setSettingValue(Setting, Parameters, Object)
206: */
207: public abstract Parameters createSettingValueParameters();
208:
209: /**
210: * Retrieves the value as configured by this framework for a certain {@link Setting} (which is
211: * always associated with a certain {@link Component}.
212: *
213: * The framework can (and should) return the default values of the Setting if it does not know
214: * what to do. It can also adminstrate overridden values, e.g. in its own configuration file.
215:
216: * Using the 'parameters' (created with {@link #createSettingValueParameters}, the Framework can also
217: * implement context specific values for a setting. It can e.g. use a request object, and store
218: * user specific value as cookies.
219: */
220: public abstract <C> C getSettingValue(Setting<C> setting,
221: Parameters parameters);
222:
223: /**
224: * See {@link #getSettingValue}. Depending on the framework, the set value may not necessarily be persistant.
225: *
226: * @throws SecurityException If you are not allowed to change the setting.
227: */
228: public abstract <C> C setSettingValue(Setting<C> setting,
229: Parameters parameters, C value) throws SecurityException;
230:
231: public abstract Parameter[] getParameterDefinition();
232:
233: /**
234: * Return a (possibly modified) URL for a given path.
235: * This method is called (for example) from within the mm:url tag, and can be exposed to the outside world.
236: * I.e. when within a components's head you use<br />
237: * <mm:url page="/css/style.css" />, <br />
238: * this method is called to determine the proper url (i.e., relative to the framework or component base).
239: * If you need treefile/leaffile type of functionality in your framework, you can implement that
240: * here in your code.
241: *
242: * @param path The path (generally a relative URL) to create an URL for.
243: * @param parameters Parameters The parameters to be passed to the page
244: * @param frameworkParameters The parameters that are required by the framework
245: * @param escapeAmps <code>true</code> if parameters should be added with an escaped & (&amp;).
246: * You should escape & when a URL is exposed (i.e. in HTML), but not if the url is
247: * for some reason called directly.
248: * @return An URL relative to the root of this web application (i.e. withouth a context path),
249: */
250: public abstract String getUrl(String path,
251: Map<String, Object> parameters,
252: Parameters frameworkParameters, boolean escapeAmps)
253: throws FrameworkException;
254:
255: public abstract String getProcessUrl(String path,
256: Map<String, Object> parameters,
257: Parameters frameworkParameters, boolean escapeAmps)
258: throws FrameworkException;
259:
260: /**
261: * Generates an URL to a resource to be called and included by a renderer.
262: * Typically, this generates a URL to a jsp, called by a renderer such as the {@link JspRenderer},
263: * who calls the resource using the RequestDispatcher.
264: * This method allows for frameworks to do some filtering on URLs (such as pretty URLs).
265: * You should generally not call this method unless you write a Renderer that depends on code or
266: * data from external resources.
267: * @param path The page (e.g. image/css) provided by the component to create an URL for
268: * @param params Extra parameters for that path
269: * @param frameworkParameters The parameters that are required by the framework, such as the
270: * 'request' and 'cloud' objects
271: * @return A valid interal URL, or <code>null</code> if nothing framework specific could be
272: * determined (this would make it possible to 'chain' frameworks).
273: */
274: public abstract String getInternalUrl(String path,
275: Map<String, Object> params, Parameters frameworkParameters)
276: throws FrameworkException;
277:
278: }
|