001: /*
002: * This file is part of PFIXCORE.
003: *
004: * PFIXCORE is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU Lesser General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * PFIXCORE is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public License
015: * along with PFIXCORE; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: */
019: package de.schlund.pfixxml.targets;
020:
021: import java.io.ByteArrayOutputStream;
022: import java.io.IOException;
023: import java.io.OutputStream;
024: import java.util.Iterator;
025: import java.util.TreeMap;
026: import java.util.TreeSet;
027:
028: import javax.xml.transform.Templates;
029: import javax.xml.transform.TransformerException;
030: import javax.xml.transform.stream.StreamResult;
031:
032: import org.apache.log4j.NDC;
033: import org.w3c.dom.Document;
034:
035: import de.schlund.pfixxml.XMLException;
036: import de.schlund.pfixxml.resources.FileResource;
037: import de.schlund.pfixxml.resources.ResourceUtil;
038: import de.schlund.pfixxml.util.Xslt;
039:
040: /**
041: * VirtualTarget.java
042: *
043: *
044: * Created: Mon Jul 23 19:53:38 2001
045: *
046: * @author <a href="mailto: "Jens Lautenbacher</a>
047: *
048: *
049: */
050: public abstract class VirtualTarget extends TargetImpl {
051: protected long modtime = 0;
052:
053: protected TreeSet<PageInfo> pageinfos = new TreeSet<PageInfo>();
054:
055: protected boolean forceupdate = false;
056:
057: /**
058: * @see de.schlund.pfixxml.targets.TargetRW#addPageInfo(de.schlund.pfixxml.targets.PageInfo)
059: */
060: public void addPageInfo(PageInfo info) {
061: synchronized (pageinfos) {
062: pageinfos.add(info);
063: }
064: }
065:
066: /**
067: * @see de.schlund.pfixxml.targets.Target#getPageInfos()
068: */
069: public TreeSet<PageInfo> getPageInfos() {
070: synchronized (pageinfos) {
071: return new TreeSet<PageInfo>(pageinfos);
072: }
073: }
074:
075: /**
076: * @see de.schlund.pfixxml.targets.TargetRW#setXMLSource(de.schlund.pfixxml.targets.Target)
077: */
078: public void setXMLSource(Target source) {
079: xmlsource = source;
080: }
081:
082: /**
083: * @see de.schlund.pfixxml.targets.TargetRW#setXSLSource(de.schlund.pfixxml.targets.Target)
084: */
085: public void setXSLSource(Target source) {
086: xslsource = source;
087: }
088:
089: /**
090: * @see de.schlund.pfixxml.targets.TargetRW#addParam(java.lang.String, java.lang.String)
091: */
092: public void addParam(String key, Object val) {
093: synchronized (params) {
094: params.put(key, val);
095: }
096: }
097:
098: /**
099: * @see de.schlund.pfixxml.targets.Target#getModTime()
100: */
101: public long getModTime() {
102: if (modtime == 0) {
103: synchronized (this ) {
104: if (modtime == 0) {
105: FileResource doc = ResourceUtil.getFileResource(
106: getTargetGenerator().getDisccachedir(),
107: getTargetKey());
108: if (doc.exists() && doc.isFile()) {
109: setModTime(doc.lastModified());
110: }
111: }
112: }
113: }
114: return modtime;
115: }
116:
117: /**
118: * @see de.schlund.pfixxml.targets.Target#needsUpdate()
119: */
120: public boolean needsUpdate() throws Exception {
121: long mymodtime = getModTime();
122: long xmlmod;
123: long xslmod;
124: long depmod = 0;
125: boolean xmlup;
126: boolean xslup;
127: boolean depup = false;
128: Target tmp;
129:
130: tmp = getXMLSource();
131: xmlup = tmp.needsUpdate();
132: xmlmod = tmp.getModTime();
133: tmp = getXSLSource();
134: xslup = tmp.needsUpdate();
135: xslmod = tmp.getModTime();
136:
137: for (Iterator<AuxDependency> i = this .getAuxDependencyManager()
138: .getChildren().iterator(); i.hasNext();) {
139: AuxDependency aux = i.next();
140: if (aux.getType() == DependencyType.TARGET) {
141: Target auxtarget = ((AuxDependencyTarget) aux)
142: .getTarget();
143: depmod = Math.max(auxtarget.getModTime(), depmod);
144: if (auxtarget.needsUpdate()) {
145: depup = true;
146: }
147: }
148: }
149:
150: if (forceupdate)
151: return true;
152: if (xslup || xmlup || depup)
153: return true;
154: if ((xmlmod > mymodtime)
155: || (xslmod > mymodtime)
156: || getAuxDependencyManager().getMaxTimestamp() > mymodtime
157: || depmod > mymodtime)
158: return true;
159: return false;
160: }
161:
162: /**
163: * @see de.schlund.pfixxml.targets.TargetRW#storeValue(java.lang.Object)
164: */
165: public void storeValue(Object obj) {
166: SPCache<Object, Object> cache = SPCacheFactory.getInstance()
167: .getCache();
168: cache.setValue(this , obj);
169: }
170:
171: public String toString() {
172: if (getXMLSource() != null && getXSLSource() != null) {
173: return "[TARGET: " + getType() + " " + getTargetKey() + "@"
174: + getTargetGenerator().getName() + "["
175: + themes.getId() + "]" + " <"
176: + getXMLSource().getTargetKey() + "> <"
177: + getXSLSource().getTargetKey() + ">]";
178: } else {
179: return "[TARGET: " + getType() + " " + getTargetKey() + "@"
180: + getTargetGenerator().getName() + "["
181: + themes.getId() + "]]";
182: }
183: }
184:
185: /**
186: * @see de.schlund.pfixxml.targets.TargetImpl#setModTime(long)
187: */
188: // still to implement from TargetImpl:
189: //protected abstract Object getValueFromDiscCache() throws Exception;
190: protected void setModTime(long mtime) {
191: modtime = mtime;
192: }
193:
194: /**
195: * @see de.schlund.pfixxml.targets.TargetImpl#getValueFromSPCache()
196: */
197: protected Object getValueFromSPCache() {
198: SPCache<Object, Object> cache = SPCacheFactory.getInstance()
199: .getCache();
200: return cache.getValue(this );
201: }
202:
203: /**
204: * @see de.schlund.pfixxml.targets.TargetImpl#getModTimeMaybeUpdate()
205: */
206: protected long getModTimeMaybeUpdate()
207: throws TargetGenerationException, XMLException, IOException {
208: // long currmodtime = getModTime();
209: long maxmodtime = 0;
210: long tmpmodtime;
211: NDC.push(" ");
212: TREE.debug("> " + getTargetKey());
213: maxmodtime = ((TargetImpl) getXMLSource())
214: .getModTimeMaybeUpdate();
215: // if (maxmodtime > currmodtime) {
216: // CAT.warn("### XMLSource of " + getTargetKey() + " is newer! " + maxmodtime + ">" + currmodtime);
217: // }
218: tmpmodtime = ((TargetImpl) getXSLSource())
219: .getModTimeMaybeUpdate();
220: // if (tmpmodtime > currmodtime) {
221: // CAT.warn("### XSLSource of " + getTargetKey() + " is newer! " + tmpmodtime + ">" + currmodtime);
222: // }
223: maxmodtime = Math.max(tmpmodtime, maxmodtime);
224: storedException = null;
225: // check all the auxilliary sources from auxsource
226: tmpmodtime = getAuxDependencyManager().getMaxTimestamp();
227: // if (tmpmodtime > currmodtime) {
228: // CAT.warn("### AUX of " + getTargetKey() + " is newer! " + tmpmodtime + ">" + currmodtime);
229: // }
230: maxmodtime = Math.max(tmpmodtime, maxmodtime);
231:
232: // check target dependencies
233: for (Iterator<AuxDependency> i = this .getAuxDependencyManager()
234: .getChildren().iterator(); i.hasNext();) {
235: AuxDependency aux = i.next();
236: if (aux.getType() == DependencyType.TARGET) {
237: Target auxtarget;
238: try {
239: auxtarget = ((AuxDependencyTarget) aux).getTarget();
240: } catch (Exception e) {
241: throw new TargetGenerationException(
242: "Nested exception", e);
243: }
244: if (auxtarget instanceof TargetImpl) {
245: tmpmodtime = ((TargetImpl) auxtarget)
246: .getModTimeMaybeUpdate();
247: } else {
248: tmpmodtime = auxtarget.getModTime();
249: }
250: maxmodtime = Math.max(tmpmodtime, maxmodtime);
251: }
252: }
253:
254: // check target generator config / navigation tree
255: tmpmodtime = getTargetGenerator().getConfigMaxModTime();
256: maxmodtime = Math.max(tmpmodtime, maxmodtime);
257:
258: if ((maxmodtime > getModTime()) || forceupdate) {
259: synchronized (this ) {
260: if ((maxmodtime > getModTime()) || forceupdate) {
261: try {
262: generateValue();
263: TREE.debug(" [" + getTargetKey()
264: + ": generated...]");
265: } catch (TransformerException e) {
266: LOG.error("Error when generating: "
267: + getTargetKey() + " from "
268: + getXMLSource().getTargetKey()
269: + " and "
270: + getXSLSource().getTargetKey(), e);
271: // Now we invalidate the mem- and disc cache to force
272: // a complete rebuild of this target the next try
273: storeValue(null);
274: setModTime(-1);
275: FileResource cachefile = ResourceUtil
276: .getFileResource(getTargetGenerator()
277: .getDisccachedir(),
278: getTargetKey());
279: if (cachefile.exists()) {
280: cachefile.delete();
281: }
282:
283: TransformerException tex = e;
284: TargetGenerationException targetex = null;
285: if (storedException != null) {
286: targetex = new TargetGenerationException(
287: "Caught transformer exception when doing getModtimeMaybeUpdate",
288: storedException);
289: } else {
290: targetex = new TargetGenerationException(
291: "Caught transformer exception when doing getModtimeMaybeUpdate",
292: tex);
293: }
294: targetex.setTargetkey(targetkey);
295: throw targetex;
296: }
297: }
298: }
299: } else {
300: TREE.debug(" [" + getTargetKey() + ": skipping...]");
301: }
302: NDC.pop();
303: return getModTime();
304: }
305:
306: private void generateValue() throws XMLException,
307: TransformerException, IOException {
308: String key = getTargetKey();
309: Target tmpxmlsource = getXMLSource();
310: Target tmpxslsource = getXSLSource();
311: FileResource cachepath = getTargetGenerator().getDisccachedir();
312: FileResource cachefile = ResourceUtil.getFileResource(
313: cachepath, key);
314: cachefile.getParentAsFileResource().mkdirs();
315: if (LOG.isDebugEnabled()) {
316: LOG.debug(key + ": Getting " + getType() + " by XSLTrafo ("
317: + tmpxmlsource.getTargetKey() + " / "
318: + tmpxslsource.getTargetKey() + ")");
319: }
320: // we reset the auxilliary dependencies here, as they will be rebuild now, too
321: getAuxDependencyManager().reset();
322: // as the file will be rebuild in the disc cache, we need to make sure that we will load it again
323: // when we need it by invalidating the Memcache;
324: storeValue(null);
325: // Ok, the value of the xml and the xsl dependency may still be null
326: // (as we defer loading until we actually need the doc, which is now).
327: // But the modtime has been taken into account, so those files exists in the disc cache and
328: // are up-to-date: getCurrValue() will finally load these values.
329: Document xmlobj = (Document) ((TargetRW) tmpxmlsource)
330: .getCurrValue();
331: Templates templ = (Templates) ((TargetRW) tmpxslsource)
332: .getCurrValue();
333: if (xmlobj == null)
334: throw new XMLException("**** xml source "
335: + tmpxmlsource.getTargetKey() + " ("
336: + tmpxmlsource.getType()
337: + ") doesn't have a value!");
338: if (templ == null)
339: throw new XMLException("**** xsl source "
340: + tmpxslsource.getTargetKey() + " ("
341: + tmpxslsource.getType()
342: + ") doesn't have a value!");
343: TreeMap<String, Object> tmpparams = getParams();
344: tmpparams.put("themes", themes.getId());
345:
346: // Store output in temporary object and overwrite cache file only
347: // when transformation was sucessfully finished
348: ByteArrayOutputStream temp = new ByteArrayOutputStream();
349: Xslt
350: .transform(xmlobj, templ, tmpparams, new StreamResult(
351: temp));
352: OutputStream out = cachefile.getOutputStream();
353: out.write(temp.toByteArray());
354: out.close();
355:
356: // Load the target in memcache to make sure all load time
357: // dependencies are being registered
358: this .getCurrValue();
359:
360: // Now we need to save the current value of the auxdependencies
361: getAuxDependencyManager().saveAuxdepend();
362: // and let's update the modification time.
363: setModTime(cachefile.lastModified());
364: forceupdate = false;
365: }
366:
367: public void setForceUpdate() {
368: forceupdate = true;
369: }
370:
371: } // VirtualTarget
|