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.cocoon.components.flow.apples.samples;
018:
019: import java.io.IOException;
020: import java.io.OutputStream;
021: import java.util.HashMap;
022: import java.util.Locale;
023: import java.util.Map;
024:
025: import javax.xml.transform.OutputKeys;
026: import javax.xml.transform.Transformer;
027: import javax.xml.transform.TransformerFactory;
028: import javax.xml.transform.sax.SAXTransformerFactory;
029: import javax.xml.transform.sax.TransformerHandler;
030: import javax.xml.transform.stream.StreamResult;
031:
032: import org.apache.avalon.framework.logger.AbstractLogEnabled;
033: import org.apache.avalon.framework.service.ServiceException;
034: import org.apache.avalon.framework.service.ServiceManager;
035: import org.apache.avalon.framework.service.Serviceable;
036: import org.apache.cocoon.ProcessingException;
037: import org.apache.cocoon.components.flow.apples.AppleController;
038: import org.apache.cocoon.components.flow.apples.AppleRequest;
039: import org.apache.cocoon.components.flow.apples.AppleResponse;
040: import org.apache.cocoon.forms.FormContext;
041: import org.apache.cocoon.forms.FormManager;
042: import org.apache.cocoon.forms.binding.Binding;
043: import org.apache.cocoon.forms.binding.BindingManager;
044: import org.apache.cocoon.forms.formmodel.Form;
045: import org.apache.cocoon.forms.transformation.FormsPipelineConfig;
046: import org.apache.cocoon.xml.dom.DOMStreamer;
047: import org.apache.excalibur.source.ModifiableSource;
048: import org.apache.excalibur.source.Source;
049: import org.apache.excalibur.source.SourceResolver;
050: import org.apache.excalibur.xml.dom.DOMParser;
051: import org.w3c.dom.Document;
052: import org.xml.sax.InputSource;
053:
054: /**
055: * BindingWoodyApple
056: */
057: public class BindingWoodyApple extends AbstractLogEnabled implements
058: AppleController, Serviceable {
059:
060: private static final boolean CONTINUE = false;
061: private static final boolean FINISHED = true;
062:
063: private Form form;
064: private Binding binding;
065: private Document document;
066: private ServiceManager serviceManager;
067: private String formPipeURI;
068: private String validPipeURI;
069: private String backendURI;
070: private Map wrapperContextMap;
071: private State state;
072:
073: private interface State {
074: public void processRequest(AppleRequest req, AppleResponse res)
075: throws ProcessingException;
076: }
077:
078: private final State initializationDelegate = new State() {
079: public void processRequest(AppleRequest req, AppleResponse res)
080: throws ProcessingException {
081: BindingWoodyApple.this .processInitialization(req, res);
082: }
083: };
084:
085: private final State validationDelegate = new State() {
086: public void processRequest(AppleRequest req, AppleResponse res)
087: throws ProcessingException {
088: BindingWoodyApple.this .processValidation(req, res);
089: }
090: };
091:
092: {
093: state = initializationDelegate;
094: }
095:
096: public void process(AppleRequest req, AppleResponse res)
097: throws ProcessingException {
098: this .state.processRequest(req, res);
099: }
100:
101: protected void processInitialization(AppleRequest req,
102: AppleResponse res) throws ProcessingException {
103:
104: String formURI = req.getSitemapParameter("form-src");
105: String bindURI = req.getSitemapParameter("binding-src");
106: this .backendURI = req.getSitemapParameter("documentURI");
107: this .formPipeURI = req.getSitemapParameter("form-pipe");
108: this .validPipeURI = req.getSitemapParameter("valid-pipe");
109:
110: FormManager formManager = null;
111: BindingManager binderManager = null;
112: SourceResolver resolver = null;
113: Source formSource = null;
114: Source bindSource = null;
115: Source documentSource = null;
116:
117: try {
118: formManager = (FormManager) this .serviceManager
119: .lookup(FormManager.ROLE);
120: binderManager = (BindingManager) this .serviceManager
121: .lookup(BindingManager.ROLE);
122: resolver = (SourceResolver) this .serviceManager
123: .lookup(SourceResolver.ROLE);
124:
125: formSource = resolver.resolveURI(formURI);
126: this .form = formManager.createForm(formSource);
127:
128: bindSource = resolver.resolveURI(bindURI);
129: this .binding = binderManager.createBinding(bindSource);
130:
131: documentSource = resolver.resolveURI(this .backendURI);
132: this .document = loadDocumentFromSource(documentSource);
133: this .binding.loadFormFromModel(this .form, this .document);
134:
135: this .getLogger().debug("apple initialisation finished .. ");
136: this .state = validationDelegate;
137:
138: completeResult(res, this .formPipeURI, CONTINUE);
139: } catch (Exception e) {
140: throw new ProcessingException(
141: "Failed to initialize BindingWoodyApple. ", e);
142: } finally {
143: if (formManager != null) {
144: this .serviceManager.release(formManager);
145: }
146: if (binderManager != null) {
147: this .serviceManager.release(binderManager);
148: }
149: if (resolver != null) {
150: if (formSource != null) {
151: resolver.release(formSource);
152: }
153: if (bindSource != null) {
154: resolver.release(bindSource);
155: }
156: if (documentSource != null) {
157: resolver.release(documentSource);
158: }
159: this .serviceManager.release(resolver);
160: }
161: }
162: }
163:
164: protected void processValidation(AppleRequest req, AppleResponse res)
165: throws ProcessingException {
166:
167: Source documentTarget = null;
168: SourceResolver resolver = null;
169:
170: try {
171: FormContext formContext = new FormContext(req
172: .getCocoonRequest(), Locale.US);
173:
174: if (!this .form.process(formContext)) {
175: // form is not valid or there was just an event handled
176: completeResult(res, this .formPipeURI, CONTINUE);
177: } else {
178:
179: resolver = (SourceResolver) this .serviceManager
180: .lookup(SourceResolver.ROLE);
181: documentTarget = resolver
182: .resolveURI(makeTargetURI(this .backendURI));
183:
184: this .binding.saveFormToModel(this .form, this .document);
185: saveDocumentToSource(documentTarget, this .document);
186:
187: completeResult(res, this .validPipeURI, FINISHED);
188: }
189:
190: getLogger().debug("apple processing done .. ");
191: } catch (Exception e) {
192: throw new ProcessingException(
193: "Error processing BindingWoodyApple", e);
194: } finally {
195: if (resolver != null) {
196: if (documentTarget != null) {
197: resolver.release(documentTarget);
198: }
199: this .serviceManager.release(resolver);
200: }
201: }
202: }
203:
204: private void completeResult(AppleResponse res, String uri,
205: boolean finished) {
206: res.sendPage(uri, getContextMap());
207: // TODO think about transferring the fact that the use case has ended.
208: }
209:
210: private Map getContextMap() {
211: if (this .wrapperContextMap == null) {
212: if (this .form != null) {
213: this .wrapperContextMap = new HashMap();
214: this .wrapperContextMap.put(
215: FormsPipelineConfig.CFORMSKEY, this .form);
216: }
217: }
218: return this .wrapperContextMap;
219: }
220:
221: /**
222: * Translate source path into target path so we keep a clean source XML
223: *
224: * @param path
225: */
226: private String makeTargetURI(String path) {
227: final String sfx = ".xml";
228: final String newSfx = "-result.xml";
229: if (path.endsWith(sfx)) {
230: path = path.substring(0, path.length() - sfx.length());
231: }
232: return path + newSfx;
233: }
234:
235: /**
236: * Saves (and serializes) the given Document to the path indicated by the
237: * specified Source.
238: *
239: * @param docTarget must be the ModifieableSource where the doc will be
240: * serialized to.
241: * @param doc org.w3c.dom.Document to save
242: * @throws ProcessingException
243: */
244: private void saveDocumentToSource(Source docTarget, Document doc)
245: throws ProcessingException {
246: DOMParser parser = null;
247: OutputStream os = null;
248: String uri = docTarget.getURI();
249:
250: try {
251: parser = (DOMParser) this .serviceManager
252: .lookup(DOMParser.ROLE);
253: getLogger().debug("request to save file " + uri);
254: TransformerFactory tf = TransformerFactory.newInstance();
255:
256: if (docTarget instanceof ModifiableSource
257: && tf.getFeature(SAXTransformerFactory.FEATURE)) {
258:
259: ModifiableSource ws = (ModifiableSource) docTarget;
260: os = ws.getOutputStream();
261: SAXTransformerFactory stf = (SAXTransformerFactory) tf;
262: TransformerHandler th = stf.newTransformerHandler();
263: Transformer t = th.getTransformer();
264: t.setOutputProperty(OutputKeys.INDENT, "true");
265: t.setOutputProperty(OutputKeys.METHOD, "xml");
266: th.setResult(new StreamResult(os));
267:
268: DOMStreamer streamer = new DOMStreamer(th);
269: streamer.stream(doc);
270: } else {
271: getLogger().error("Cannot not write to source " + uri);
272: }
273: } catch (Exception e) {
274: getLogger().error("Error parsing mock file " + uri, e);
275: throw new ProcessingException("Error parsing mock file "
276: + uri, e);
277: } finally {
278: if (parser != null) {
279: this .serviceManager.release(parser);
280: }
281: if (os != null) {
282: try {
283: os.flush();
284: os.close();
285: } catch (IOException e1) {
286: getLogger().warn(
287: "Failed to flush/close the outputstream. ",
288: e1);
289: }
290: }
291: }
292: }
293:
294: /**
295: * Loads (and parses) the Document from the specified Source
296: *
297: * @param documentSrc
298: * @throws ProcessingException
299: */
300: private Document loadDocumentFromSource(Source documentSrc)
301: throws ProcessingException {
302: DOMParser parser = null;
303: try {
304: parser = (DOMParser) this .serviceManager
305: .lookup(DOMParser.ROLE);
306: getLogger().debug(
307: "request to load file " + documentSrc.getURI());
308: InputSource input = new InputSource(documentSrc.getURI());
309: return parser.parseDocument(input);
310: } catch (Exception e) {
311: throw new ProcessingException(
312: "failed to load file to bind to: ", e);
313: } finally {
314: if (parser != null) {
315: this .serviceManager.release(parser);
316: }
317: }
318: }
319:
320: public void service(ServiceManager serviceManager)
321: throws ServiceException {
322: this.serviceManager = serviceManager;
323: }
324: }
|