001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.lenya.cms.cocoon.components.modules.input;
018:
019: import java.io.IOException;
020: import java.net.MalformedURLException;
021: import java.util.Enumeration;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.SortedSet;
026: import java.util.TreeSet;
027:
028: import javax.xml.parsers.DocumentBuilder;
029: import javax.xml.parsers.DocumentBuilderFactory;
030: import javax.xml.parsers.ParserConfigurationException;
031:
032: import org.apache.avalon.framework.activity.Initializable;
033: import org.apache.avalon.framework.configuration.Configuration;
034: import org.apache.avalon.framework.configuration.ConfigurationException;
035: import org.apache.avalon.framework.service.ServiceException;
036: import org.apache.avalon.framework.service.ServiceManager;
037: import org.apache.avalon.framework.service.Serviceable;
038: import org.apache.avalon.framework.thread.ThreadSafe;
039: import org.apache.cocoon.components.modules.input.DefaultsModule;
040: import org.apache.cocoon.components.modules.input.InputModule;
041: import org.apache.commons.lang.SystemUtils;
042: import org.apache.excalibur.source.Source;
043: import org.apache.excalibur.source.SourceResolver;
044: import org.apache.forrest.conf.AntProperties;
045: import org.apache.lenya.cms.publication.Publication;
046: import org.apache.lenya.cms.publication.PublicationUtil;
047: import org.apache.lenya.cms.module.ModuleManager;
048: import org.w3c.dom.Document;
049: import org.w3c.dom.Element;
050: import org.w3c.dom.NodeList;
051: import org.xml.sax.SAXException;
052:
053: /**
054: * Input module for accessing the base properties used in Lenya. The main values
055: * are the locations of the <b>source </b> directories and of the <b>Lenya </b>
056: * directories.
057: */
058: public class PropertiesModule extends DefaultsModule implements
059: InputModule, Initializable, ThreadSafe, Serviceable {
060:
061: private HashSet pubInit;
062:
063: private AntProperties filteringProperties;
064:
065: private SourceResolver m_resolver;
066:
067: private ModuleManager moduleManager;
068:
069: private ServiceManager serviceManager;
070:
071: private final static String lenyaHome = "context:/";
072:
073: private final static String DEFAULT_HOME_PROP = "lenya.home";
074:
075: private final static String PROPERTY_NAME = "lenya.properties.xml";
076:
077: private final static String PROPERTY_NAME_LOCAL = "local."
078: + PROPERTY_NAME;
079:
080: public Object getAttribute(String name, Configuration modeConf,
081: Map objectModel) throws ConfigurationException {
082: String attributeValue;
083:
084: loadPublicationPropertiesIfNotDone(objectModel);
085: attributeValue = filteringProperties.getProperty(name);
086: if (attributeValue == null) {
087: String error = "Unable to get attribute value for "
088: + name
089: + ".\n"
090: + "Please make sure you defined "
091: + name
092: + " in lenya.properties.xml either in $LENYA_HOME, $PUB_HOME or "
093: + "in the module that is requesting this property.\n"
094: + "If you see this message, most of the time you spotted a module bug "
095: + "(forget to define the default property). Please report it to "
096: + "our mailing list.";
097: throw new ConfigurationException(error);
098: }
099:
100: if (debugging()) {
101: debug(" - Requested:" + name);
102: debug(" - Given:" + attributeValue);
103: }
104:
105: return attributeValue;
106: }
107:
108: public Object[] getAttributeValues(String name,
109: Configuration modeConf, Map objectModel)
110: throws ConfigurationException {
111: loadPublicationPropertiesIfNotDone(objectModel);
112: Object[] attributeValues = super .getAttributeValues(name,
113: modeConf, objectModel);
114: for (int i = 0; i < attributeValues.length; i++) {
115: attributeValues[i] = filteringProperties
116: .filter(attributeValues[i].toString());
117: }
118:
119: return attributeValues;
120: }
121:
122: public Iterator getAttributeNames(Configuration modeConf,
123: Map objectModel) throws ConfigurationException {
124: loadPublicationPropertiesIfNotDone(objectModel);
125: SortedSet matchset = new TreeSet();
126: Enumeration enumeration = filteringProperties.keys();
127: while (enumeration.hasMoreElements()) {
128: String key = (String) enumeration.nextElement();
129: matchset.add(key);
130: }
131: Iterator iterator = super .getAttributeNames(modeConf,
132: objectModel);
133: while (iterator.hasNext())
134: matchset.add(iterator.next());
135: return matchset.iterator();
136: }
137:
138: public void initialize() throws Exception {
139:
140: pubInit = new HashSet();
141:
142: // add all homes important to Lenya to the properties
143: setHomes();
144:
145: loadSystemProperties(filteringProperties);
146:
147: // NOTE: the first values set get precedence, as in AntProperties
148: //
149: // Order of precedence:
150: // 1. Publication (lazy loaded in loadPublicationPropertiesIfNotDone())
151: // 2. Lenya local
152: // 3. Modules (all modules, not only the ones referenced in the publication)
153: // 4. Lenya
154: //
155: String lenyaPropertiesStringURI = "";
156:
157: try {
158: // get the values from local.lenya.properties.xml
159: lenyaPropertiesStringURI = lenyaHome
160: + SystemUtils.FILE_SEPARATOR + PROPERTY_NAME_LOCAL;
161: filteringProperties = loadXMLPropertiesFromURI(
162: filteringProperties, lenyaPropertiesStringURI,
163: false);
164:
165: // get the values from all modules
166: String[] module2src = moduleManager.getModuleIds();
167: for (int i = 0; i < module2src.length; i++) {
168: String id = module2src[i];
169: Object value = moduleManager.getBaseURI(id);
170: if (value != null) {
171: lenyaPropertiesStringURI = value
172: + SystemUtils.FILE_SEPARATOR
173: + PROPERTY_NAME;
174: filteringProperties = loadXMLPropertiesFromURI(
175: filteringProperties,
176: lenyaPropertiesStringURI, false);
177: }
178: }
179: // get the values from lenya.properties.xml this are the default
180: // lenya values
181: lenyaPropertiesStringURI = lenyaHome
182: + SystemUtils.FILE_SEPARATOR + PROPERTY_NAME;
183: filteringProperties = loadXMLPropertiesFromURI(
184: filteringProperties, lenyaPropertiesStringURI,
185: false);
186: } finally {
187: if (debugging())
188: debug("Loaded project lenya.properties.xml:"
189: + filteringProperties);
190: }
191:
192: }
193:
194: /**
195: * Sets all Lenya related home locations such as - LenyaHome - projectHome -
196: * contextHome
197: *
198: * @throws Exception
199: */
200: private void setHomes() throws Exception {
201:
202: filteringProperties = new AntProperties();
203: filteringProperties.setProperty(DEFAULT_HOME_PROP, lenyaHome);
204: }
205:
206: /**
207: * Override any properties for which a system property exists
208: */
209: private void loadSystemProperties(AntProperties props) {
210: for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
211: String propName = (String) e.nextElement();
212: String systemPropValue = System.getProperty(propName);
213: if (systemPropValue != null) {
214: overwriteProperty(props, propName, systemPropValue);
215: }
216: }
217: }
218:
219: private void overwriteProperty(AntProperties props,
220: String propName, String propValue) {
221: //
222: // AntProperties.setProperty doesn't let you override, so we
223: // have to remove the property then add it again
224: props.remove(propName);
225: props.setProperty(propName, propValue);
226: }
227:
228: /**
229: * @param precedingProperties
230: * @param propertiesStringURI
231: * @param overwrite
232: * @throws IOException
233: * @throws MalformedURLException
234: * @throws ParserConfigurationException
235: * @throws SAXException
236: */
237: private AntProperties loadXMLPropertiesFromURI(
238: AntProperties precedingProperties,
239: String propertiesStringURI, boolean overwrite)
240: throws MalformedURLException, IOException,
241: ParserConfigurationException, SAXException {
242:
243: Source source = null;
244: try {
245:
246: source = m_resolver.resolveURI(propertiesStringURI);
247:
248: if (source.exists()) {
249:
250: DocumentBuilderFactory factory = DocumentBuilderFactory
251: .newInstance();
252: DocumentBuilder builder = factory.newDocumentBuilder();
253: Document document = builder.parse(source.getURI());
254:
255: NodeList nl = document.getElementsByTagName("property");
256: if (nl != null && nl.getLength() > 0) {
257: for (int i = 0; i < nl.getLength(); i++) {
258: Element el = (Element) nl.item(i);
259: if (overwrite == true) {
260: overwriteProperty(filteringProperties, el
261: .getAttribute("name"), el
262: .getAttribute("value"));
263: } else {
264: filteringProperties.setProperty(el
265: .getAttribute("name"), el
266: .getAttribute("value"));
267: }
268: }
269: }
270:
271: if (debugging())
272: debug("Loaded:" + propertiesStringURI
273: + filteringProperties.toString());
274:
275: }
276:
277: } finally {
278: if (source != null) {
279: m_resolver.release(source);
280: }
281: }
282:
283: return filteringProperties;
284: }
285:
286: /**
287: * Get the properties from the requested publication
288: */
289: private void loadPublicationPropertiesIfNotDone(Map objectModel)
290: throws ConfigurationException {
291: Publication pub;
292: String pubId;
293:
294: try {
295: pub = PublicationUtil.getPublication(serviceManager,
296: objectModel);
297: } catch (Exception e) {
298: throw new ConfigurationException(e.getMessage());
299: }
300: pubId = pub.getId();
301: if (pubInit.contains(pubId)) {
302: return;
303: }
304: try {
305: filteringProperties = loadXMLPropertiesFromURI(
306: filteringProperties, PROPERTY_NAME, true);
307: } catch (IOException e) {
308: getLogger().warn(
309: "Could not load properties from pub \"" + pubId
310: + "\".\n" + e);
311: } catch (Exception e) {
312: throw new ConfigurationException(e.getMessage());
313: }
314: pubInit.add(pubId);
315: }
316:
317: public void service(ServiceManager manager) throws ServiceException {
318: this .serviceManager = manager;
319: m_resolver = (SourceResolver) manager
320: .lookup(SourceResolver.ROLE);
321: moduleManager = (ModuleManager) manager
322: .lookup(ModuleManager.ROLE);
323: }
324:
325: /**
326: * Rocked science
327: */
328: private final boolean debugging() {
329: return getLogger().isDebugEnabled();
330: }
331:
332: /**
333: * Rocked science
334: *
335: * @param debugString
336: */
337: private final void debug(String debugString) {
338: getLogger().debug(debugString);
339: }
340:
341: }
|