001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 2005 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: ApplyArchiveConfigAction.java 9680 2006-10-06 12:08:33Z danesa $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas.webapp.jonasadmin.xml;
025:
026: import java.io.ByteArrayInputStream;
027: import java.io.IOException;
028: import java.io.OutputStream;
029: import java.util.ArrayList;
030: import java.util.Arrays;
031: import java.util.Collections;
032: import java.util.Iterator;
033: import java.util.Map;
034:
035: import javax.management.ObjectName;
036: import javax.servlet.ServletException;
037: import javax.servlet.http.HttpServletRequest;
038: import javax.servlet.http.HttpServletResponse;
039: import javax.xml.parsers.DocumentBuilder;
040: import javax.xml.parsers.DocumentBuilderFactory;
041:
042: import org.apache.struts.action.ActionForm;
043: import org.apache.struts.action.ActionForward;
044: import org.apache.struts.action.ActionMapping;
045: import org.apache.struts.action.ActionMessage;
046: import org.objectweb.jonas.jmx.JonasManagementRepr;
047: import org.objectweb.jonas.webapp.jonasadmin.JonasAdminJmx;
048: import org.objectweb.jonas.webapp.jonasadmin.JonasBaseAction;
049: import org.objectweb.jonas.webapp.jonasadmin.xml.xs.ElementRestrictions;
050: import org.objectweb.jonas.webapp.jonasadmin.xml.xs.SchemaRestrictions;
051: import org.objectweb.jonas.webapp.jonasadmin.xml.xs.SchemaRestrictionsFactory;
052: import org.objectweb.jonas_lib.xml.XMLSerializer;
053: import org.w3c.dom.Document;
054: import org.w3c.dom.Element;
055: import org.w3c.dom.Node;
056: import org.w3c.dom.NodeList;
057: import org.xml.sax.EntityResolver;
058:
059: /**
060: * Action to process the input when either the advanced or the form view of the
061: * archive configuration feature is submitted.
062: *
063: * If the user selected the 'Apply' button the action validates and saves the
064: * updated deployment descriptor.
065: *
066: * If the user selected the 'Switch to Form/Advanced" the input is validated
067: * updated in the form and the user is forwarded to the other view.
068: *
069: * @author Patrick Smith
070: * @author Gregory Lapouchnian
071: */
072: public class ApplyArchiveConfigAction extends JonasBaseAction {
073:
074: /**
075: * Executes the struts action.
076: *
077: * @param p_Mapping the struts action mapping.
078: * @param p_Form the struts action form.
079: * @param p_Request the HttpServletRequest.
080: * @param p_Response the HttpServletResponse.
081: * @throws IOException
082: * @throws ServletException
083: * @return the action forward to forward to.
084: */
085: public ActionForward executeAction(ActionMapping p_Mapping,
086: ActionForm p_Form, HttpServletRequest p_Request,
087: HttpServletResponse p_Response) throws IOException,
088: ServletException {
089:
090: // Form used
091: ArchiveConfigForm oForm = (ArchiveConfigForm) p_Form;
092:
093: // Current JOnAS server
094: String serverName = m_WhereAreYou.getCurrentJonasServerName();
095: /**
096: * Where the user should be forwarded to display the confirmation page.
097: */
098: String confirmForward = "Archive Config Confirm";
099: /**
100: * Where the user should be returned to when an error occurs.
101: */
102: String currentPageForward = getCurrentPageForward(oForm);
103: /**
104: * Where the user should be forwarded to in case of switching views.
105: */
106: String switchForward = getSwitchForward(oForm);
107:
108: /**
109: * The forward to the archive selection page.
110: */
111: String selectForward = "Archive Config Select";
112:
113: // if the operation was cancelled
114: if (oForm.getCancel() != null) {
115: oForm.setCancel(null);
116: return (p_Mapping.findForward(selectForward));
117: }
118:
119: try {
120: if (oForm.getConfigType() == ArchiveConfigForm.FORM) {
121: processFormInput(oForm, p_Mapping, p_Request,
122: serverName);
123: } else {
124: processTextAreaInput(oForm, p_Mapping, p_Request,
125: serverName);
126: }
127: } catch (Exception e) {
128: return reportError(currentPageForward, oForm,
129: "An error occured when procesing the input. "
130: + e.getMessage(), p_Mapping, p_Request);
131: }
132:
133: // if we are switching to another view we don't try to save the document
134: if (oForm.getSwitchTo() != null) {
135:
136: // reset the switch button value
137: oForm.setSwitchTo(null);
138:
139: return (p_Mapping.findForward(switchForward));
140: }
141:
142: // reset the submit button value
143: oForm.setSubmit(null);
144:
145: try {
146: saveChanges(oForm, serverName);
147: } catch (Exception e) {
148: return reportError(currentPageForward, oForm,
149: "An error occured when trying to save changed. "
150: + e.getMessage(), p_Mapping, p_Request);
151: }
152:
153: // Forward to the confirm jsp.
154: return (p_Mapping.findForward(confirmForward));
155: }
156:
157: private void processFormInput(ArchiveConfigForm oForm,
158: ActionMapping p_Mapping, HttpServletRequest p_Request,
159: String serverName) throws Exception {
160: // used to keep track of added and removed elements while processing
161: // form input
162: ArrayList addedList = new ArrayList();
163: ArrayList removedList = new ArrayList();
164:
165: Map mappings = oForm.getMapping();
166: Map values = oForm.getValuesMap();
167:
168: Iterator i = values.keySet().iterator();
169: while (i.hasNext()) {
170:
171: String key = (String) i.next();
172:
173: if (mappings.containsKey(key)) {
174: Node node = ((Node) mappings.get(key));
175:
176: // wasn't removed, just need to update the values
177: if (!values.containsKey(key + "-removed")) {
178: node.setNodeValue((String) values.get(key));
179: } else { // this node was removed by the user
180: node.getParentNode().removeChild(node);
181: removedList.add(key);
182: }
183:
184: } else if (values.containsKey(key + "-parent")) {
185: String parentID = (String) values.get(key + "-parent");
186: String name = (String) values.get(key + "-name");
187: SchemaRestrictions restrictions = (new SchemaRestrictionsFactory())
188: .getSchemaRestrictions(SchemaRestrictions.RAR_TYPE);
189:
190: Node parentNode = (Node) mappings.get(parentID);
191: // if we need to insert the child in a specific location
192: ElementRestrictions er = restrictions
193: .getElementRestrictions(parentNode
194: .getNodeName());
195:
196: Element newElement = ((Document) oForm.getDocument())
197: .createElementNS(parentNode.getNamespaceURI(),
198: name);
199: if (!restrictions.getComplexElements().contains(name)) {
200: newElement.appendChild(oForm.getDocument()
201: .createTextNode((String) values.get(key)));
202: }
203:
204: if (er.isSequence()) {
205: int elIndex = er.getChildren().indexOf(
206: newElement.getNodeName());
207:
208: if (parentNode.getChildNodes().getLength() == 0
209: || elIndex == er.getChildren().size()) {
210: parentNode.appendChild(newElement);
211: } else {
212: int insertionIndex = findInsertionIndex(er,
213: parentNode.getChildNodes(), newElement);
214:
215: if (insertionIndex < 0) {
216: parentNode.appendChild(newElement);
217: } else {
218: String insertBefore = (String) er
219: .getChildren().get(insertionIndex);
220: for (int j = 0; j < parentNode
221: .getChildNodes().getLength(); j++) {
222: Node current = parentNode
223: .getChildNodes().item(j);
224: if (current.getNodeName().equals(
225: insertBefore)) {
226: parentNode.insertBefore(newElement,
227: current);
228: break;
229: }
230: }
231: }
232: }
233: } else {
234: // not a sequence, we can simply append
235: parentNode.appendChild(newElement);
236: }
237: mappings.put(key, newElement);
238: addedList.add(key);
239: }
240: // else nothing to update for this key.
241: }
242:
243: // Remove the '-parent' and '-name' keys and values of the
244: // elements that have been added to the document.
245: for (int k = 0; k < addedList.size(); k++) {
246: values.remove(addedList.get(k) + "-parent");
247: values.remove(addedList.get(k) + "-name");
248: }
249:
250: // Remove the '-removed' keys and values of the elements
251: // that have been removed from the document.
252: for (int k = 0; k < removedList.size(); k++) {
253: values.remove(removedList.get(k) + "-removed");
254: }
255:
256: // remove occurences of 
 that appear after serializing
257: // the document
258: String xml = serializeDocument(oForm.getDocument());
259: xml = xml.replaceAll("
", "");
260: // update the value of the raw XML in the form
261: oForm.setXml(xml);
262:
263: validateDocument(oForm.getDocument(), serverName);
264: }
265:
266: private int findInsertionIndex(ElementRestrictions er,
267: NodeList list, Element newElement) {
268: ArrayList a = new ArrayList();
269: for (int i = 0; i < list.getLength(); i++) {
270: if (list.item(i).getNodeType() == Node.ELEMENT_NODE) {
271: a.add(new Integer(er.getChildren().indexOf(
272: list.item(i).getNodeName())));
273: }
274: }
275: Integer newIndex = new Integer(er.getChildren().indexOf(
276: newElement.getNodeName()));
277: a.add(newIndex);
278:
279: Collections.sort(a);
280: int result = a.indexOf(newIndex);
281: if (result == a.size() - 1)
282: return -1;
283: else
284: return ((Integer) a.get(result + 1)).intValue();
285: }
286:
287: /**
288: * Save the modified document.
289: * @param oForm the Struts from which stores the document.
290: */
291: private void saveChanges(ArchiveConfigForm oForm, String serverName)
292: throws Exception {
293: // we are not switching views, but want to save the changes
294: ObjectName on = JonasAdminJmx
295: .getRarConfigObjectName(serverName);
296:
297: Object[] params = new Object[] { oForm.getArchiveName(),
298: oForm.getPathName(), oForm.getDocument() };
299:
300: String[] sig = new String[] { "java.lang.String",
301: "java.lang.String", "org.w3c.dom.Document" };
302:
303: JonasManagementRepr.invoke(on, "saveXML", params, sig,
304: serverName);
305: }
306:
307: /**
308: * Processes the updated XML passed from the <textarea>
309: * component and updates the Document representation.
310: *
311: * @param oForm the Struts form
312: * @param p_Mapping the struts action mapping.
313: * @param p_Request the HttpServletRequest.
314: * @throws Exception if the XML is not well-formed or not valid
315: */
316: private void processTextAreaInput(ArchiveConfigForm oForm,
317: ActionMapping p_Mapping, HttpServletRequest p_Request,
318: String serverName) throws Exception {
319: String xml = oForm.getXml();
320:
321: ByteArrayInputStream str = new ByteArrayInputStream(xml
322: .getBytes());
323: Document doc;
324:
325: DocumentBuilderFactory factory = DocumentBuilderFactory
326: .newInstance();
327: factory.setNamespaceAware(true);
328: DocumentBuilder builder = factory.newDocumentBuilder();
329:
330: // Get the entity resolver for RAR DDs from the MBean
331: EntityResolver jer;
332: ObjectName on = JonasAdminJmx
333: .getRarConfigObjectName(serverName);
334: jer = (EntityResolver) JonasManagementRepr.invoke(on,
335: "getEntityResolver", new Object[] {}, new String[] {},
336: serverName);
337:
338: builder.setEntityResolver(jer);
339: doc = builder.parse(str);
340: str.close();
341:
342: // set the document to the Document object just parsed from the
343: // textarea input
344: oForm.setDocument(doc);
345:
346: validateDocument(oForm.getDocument(), serverName);
347: }
348:
349: /**
350: * Get the ActionForward name for the current page.
351: * @param form Struts form
352: * @return ActionForward for the current page
353: */
354: protected String getCurrentPageForward(ArchiveConfigForm form) {
355: if (form.getConfigType() == ArchiveConfigForm.FORM) {
356: return "Archive Config";
357: } else {
358: return "Archive Config Advanced";
359: }
360: }
361:
362: /**
363: * Get the ActionForward name to switch views from the current page.
364: * @param form Struts form
365: * @return ActionForward the user should be forwarded to if they
366: * are switching views
367: */
368: protected String getSwitchForward(ArchiveConfigForm form) {
369: if (form.getConfigType() == ArchiveConfigForm.FORM) {
370: return "Archive Config Advanced";
371: } else {
372: return "Archive Config";
373: }
374: }
375:
376: /**
377: * Report an error and forward to the given JSP page.
378: *
379: * @param sForward what JSP page to forward to
380: * @param oForm the form bean
381: * @param errorMessage the message from the exception
382: * @param p_Mapping
383: * @param p_Request
384: * @return ActionForward found for the given forward string
385: */
386: protected ActionForward reportError(String sForward,
387: ArchiveConfigForm oForm, String errorMessage,
388: ActionMapping p_Mapping, HttpServletRequest p_Request) {
389:
390: m_Errors.add("error.archiveconfig.valid.fail",
391: new ActionMessage("error.archiveconfig.valid.fail",
392: errorMessage));
393: saveErrors(p_Request, m_Errors);
394:
395: // reset the values fo the buttons
396: oForm.setSwitchTo(null);
397: oForm.setSubmit(null);
398:
399: // forward back to the advanced view to display the errors
400: return (p_Mapping.findForward(sForward));
401: }
402:
403: /**
404: * Serialize a DOM tree into plaint text.
405: *
406: * @param doc Document tree to convert
407: * @return a string representation of the DOM tree
408: */
409: protected String serializeDocument(Document doc) {
410: XMLSerializer ser = new XMLSerializer(doc);
411: ser.setIndent(4);
412: ser.setLineWidth(0);
413:
414: OutputStream out = new java.io.ByteArrayOutputStream();
415: try {
416: ser.serialize(out);
417: } catch (IOException e) {
418: e.printStackTrace();
419: }
420: return out.toString();
421: }
422:
423: /**
424: * Validate the document using appropriate schemas.
425: *
426: * @param doc the document that needs to be validated
427: * @throws Exception an exception if the document is not valid
428: */
429: protected void validateDocument(Document doc, String serverName)
430: throws Exception {
431: ObjectName on = JonasAdminJmx
432: .getRarConfigObjectName(serverName);
433: Object[] params = new Object[] { doc };
434: String[] sig = new String[] { "org.w3c.dom.Document" };
435:
436: JonasManagementRepr.invoke(on, "verifyDocument", params, sig,
437: serverName);
438: }
439: }
|