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.jetspeed.serializer;
018:
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.FileOutputStream;
022: import java.sql.Date;
023: import java.util.ArrayList;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.Map;
027: import java.util.StringTokenizer;
028:
029: import javolution.xml.XMLBinding;
030: import javolution.xml.XMLObjectReader;
031: import javolution.xml.XMLObjectWriter;
032:
033: import org.apache.commons.configuration.Configuration;
034: import org.apache.commons.configuration.PropertiesConfiguration;
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037: import org.apache.jetspeed.Jetspeed;
038: import org.apache.jetspeed.components.ComponentManager;
039: import org.apache.jetspeed.components.SpringComponentManager;
040: import org.apache.jetspeed.engine.JetspeedEngineConstants;
041: import org.apache.jetspeed.serializer.objects.JSSnapshot;
042:
043: public abstract class JetspeedSerializerBase {
044:
045: /** Logger */
046: protected static final Log log = LogFactory
047: .getLog(JetspeedSerializer.class);
048:
049: private ComponentManager cm = null;
050: private Object sem = new Object();
051:
052: int refCouter = 0;
053:
054: /** the main wrapper class for an XML file */
055: private JSSnapshot snapshot;
056:
057: /** processing flags */
058: /** export/import instructions */
059:
060: private HashMap processSettings = new HashMap();
061:
062: private boolean initialized = false;
063:
064: /** current indent for XML files - defaults to tab */
065: private String currentIndent = null;
066:
067: private static String ENCODING_STRING = "JETSPEED 2.1 - 2006";
068: private static String JETSPEED = "JETSPEED";
069:
070: protected final ComponentManager getCM() {
071: if (cm == null) {
072: cm = Jetspeed.getComponentManager();
073: }
074: return cm;
075: }
076:
077: public JetspeedSerializerBase() {
078: }
079:
080: /**
081: * hand over existing component manager
082: *
083: * @param cm
084: */
085: public JetspeedSerializerBase(ComponentManager cm) {
086: this .setComponentManager(cm);
087: this .initialized = true;
088: }
089:
090: /**
091: * This constructor takes the application root, the search path for the boot
092: * component configuration files and the search path for the application
093: * component configuration files.
094: * <p>
095: * For example: new JetspeedSerializerSecondaryImpl("./", "assembly/boot/*.xml",
096: * "assembly/*.xml") will establish the current directory as the root,
097: * process all xml files in the assembly/boot directory before processing
098: * all xml files in the assembly directory itself.
099: *
100: * @param appRoot
101: * working directory
102: * @param bootConfig
103: * boot (primary) file or files (wildcards are allowed)
104: * @param appConfig
105: * application (secondary) file or files (wildcards are allowed)
106: */
107: public JetspeedSerializerBase(String appRoot, String[] bootConfig,
108: String[] appConfig) throws SerializerException {
109: this .initializeComponentManager(appRoot, bootConfig, appConfig);
110: this .initialized = true;
111: }
112:
113: /*
114: * (non-Javadoc)
115: *
116: * @see org.apache.jetspeed.serializer.JetspeedSerializer#nitializeComponentManager(String,String[],String[])
117: */
118: public final void initializeComponentManager(String appRoot,
119: String[] bootConfig, String[] appConfig)
120: throws SerializerException {
121:
122: if (this .initialized)
123: throw new SerializerException(
124: SerializerException.COMPONENT_MANAGER_EXISTS
125: .create(""));
126: SpringComponentManager cm = new SpringComponentManager(
127: bootConfig, appConfig, appRoot);
128: cm.start();
129: Configuration properties = new PropertiesConfiguration();
130: properties.setProperty(
131: JetspeedEngineConstants.APPLICATION_ROOT_KEY, appRoot);
132: this .setComponentManager(cm);
133: }
134:
135: /*
136: * (non-Javadoc)
137: *
138: * @see org.apache.jetspeed.serializer.JetspeedSerializer#setComponentManager(ComponentManager)
139: */
140: public final void setComponentManager(ComponentManager cm) {
141: this .cm = cm;
142: }
143:
144: /*
145: * (non-Javadoc)
146: *
147: * @see org.apache.jetspeed.serializer.JetspeedSerializer#closeUp()
148: */
149: public final void closeUp() {
150: if (cm != null)
151: cm.stop();
152: cm = null;
153: }
154:
155: /*
156: * (non-Javadoc)
157: *
158: * @see org.apache.jetspeed.serializer.JetspeedSerializer#setDefaultIndent(String)
159: */
160: public final void setDefaultIndent(String indent) {
161: this .currentIndent = indent;
162: }
163:
164: /*
165: * (non-Javadoc)
166: *
167: * @see org.apache.jetspeed.serializer.JetspeedSerializer#getDefaultIndent()
168: */
169: public final String getDefaultIndent() {
170: return this .currentIndent;
171: }
172:
173: /*
174: * (non-Javadoc)
175: *
176: * @see org.apache.jetspeed.serializer.JetspeedSerializer#importData(String,
177: * Map)
178: */
179: public final void importData(String importFileName, Map settings)
180: throws SerializerException {
181: if (cm == null) {
182: cm = Jetspeed.getComponentManager();
183: }
184: /** pre-processing homework... */
185: XMLBinding binding = new XMLBinding();
186: setupAliases(binding);
187: checkSettings(settings);
188: setSnapshot(readFile(importFileName, binding));
189: if (getSnapshot() == null)
190: throw new SerializerException(
191: SerializerException.FILE_PROCESSING_ERROR
192: .create(new String[] { importFileName,
193: "Snapshot is NULL" }));
194:
195: if (!(getSnapshot().checkVersion()))
196: throw new SerializerException(
197: SerializerException.INCOMPETIBLE_VERSION
198: .create(new String[] {
199: importFileName,
200: String.valueOf(getSnapshot()
201: .getSoftwareVersion()),
202: String.valueOf(getSnapshot()
203: .getSavedSubversion()) }));
204:
205: /** ok, now we have a valid snapshot and can start processing it */
206:
207: /** ensure we can work undisturbed */
208: synchronized (sem) {
209: logMe("*********Reading data*********");
210: this .processImport();
211: }
212: return;
213: }
214:
215: /*
216: * (non-Javadoc)
217: *
218: * @see org.apache.jetspeed.serializer.JetspeedSerializer#exportData(String,String,Map)
219: */
220: public final void exportData(String name, String exportFileName,
221: Map settings) throws SerializerException {
222: /** pre-processing homework... */
223: XMLBinding binding = new XMLBinding();
224: setupAliases(binding);
225: checkSettings(settings);
226: if (cm == null) {
227: cm = Jetspeed.getComponentManager();
228: }
229: /** ensure we can work undisturbed */
230: synchronized (sem) {
231: /** get the snapshot construct */
232: this .processExport(name, binding);
233: XMLObjectWriter writer = openWriter(exportFileName);
234: writer.setBinding(binding);
235:
236: if (this .getDefaultIndent() != null)
237: writer.setIndentation(this .getDefaultIndent());
238:
239: try {
240: logMe("*********Writing data*********");
241: writer.write(getSnapshot(), getSerializerDataTag(),
242: getSerializerDataClass());
243:
244: } catch (Exception e) {
245: throw new SerializerException(
246: SerializerException.FILE_PROCESSING_ERROR
247: .create(new String[] { exportFileName,
248: e.getMessage() }));
249: } finally {
250: /** ensure the writer is closed */
251: try {
252: logMe("*********closing up********");
253: writer.close();
254: } catch (Exception e) {
255: logMe("Error in closing writer " + e.getMessage());
256: /**
257: * don't do anything with this exception - never let the
258: * bubble out of the finally block
259: */
260: }
261: }
262: }
263: return;
264: }
265:
266: /**
267: * create a backup of the current environment in case the import fails
268: *
269: */
270: protected final void doBackupOfCurrent(String importFileName,
271: Map currentSettings) {
272: // TODO: HJB create backup of current content
273: }
274:
275: /**
276: * read a snapshot and return the reconstructed class tree
277: *
278: * @param importFileName
279: * @throws SerializerException
280: */
281:
282: /**
283: * read a snapshot and return the reconstructed class tree
284: *
285: * @param importFileName
286: * @throws SerializerException
287: */
288:
289: protected final JSSnapshot readFile(String importFileName,
290: XMLBinding binding) throws SerializerException {
291: XMLObjectReader reader = null;
292: JSSnapshot snap = null;
293: try {
294: reader = XMLObjectReader.newInstance(new FileInputStream(
295: importFileName));
296: } catch (Exception e) {
297: throw new SerializerException(
298: SerializerException.FILE_READER_ERROR
299: .create(new String[] { importFileName,
300: e.getMessage() }));
301: }
302: try {
303: if (binding != null)
304: reader.setBinding(binding);
305: snap = (JSSnapshot) reader.read(
306: this .getSerializerDataTag(),
307: getSerializerDataClass());
308:
309: } catch (Exception e) {
310: e.printStackTrace();
311: new SerializerException(
312: SerializerException.FILE_PROCESSING_ERROR
313: .create(new String[] { importFileName,
314: e.getMessage() }));
315: } finally {
316: /** ensure the reader is closed */
317: try {
318: logMe("*********closing up reader ********");
319: reader.close();
320: } catch (Exception e1) {
321: logMe("Error in closing reader " + e1.getMessage());
322: /**
323: * don't do anything with this exception - never let the bubble
324: * out of the finally block
325: */
326: return null;
327: }
328: }
329: return snap;
330: }
331:
332: /**
333: * create or open a given file for writing
334: */
335: protected final XMLObjectWriter openWriter(String filename)
336: throws SerializerException {
337: File f;
338:
339: try {
340: f = new File(filename);
341: } catch (Exception e) {
342: throw new SerializerException(
343: SerializerException.FILE_PROCESSING_ERROR
344: .create(new String[] { filename,
345: e.getMessage() }));
346: }
347: boolean exists = f.exists();
348:
349: if (exists) {
350: if (!(this
351: .getSetting(JetspeedSerializer.KEY_OVERWRITE_EXISTING)))
352: throw new SerializerException(
353: SerializerException.FILE_ALREADY_EXISTS
354: .create(filename));
355: if (this
356: .getSetting(JetspeedSerializer.KEY_BACKUP_BEFORE_PROCESS)) {
357: String backName = createUniqueBackupFilename(f
358: .getName());
359: if (backName == null)
360: throw new SerializerException(
361: SerializerException.FILE_BACKUP_FAILED
362: .create(filename));
363: File ftemp = new File(backName);
364: f.renameTo(ftemp);
365: }
366: }
367: try {
368: XMLObjectWriter writer = XMLObjectWriter
369: .newInstance(new FileOutputStream(filename));
370: return writer;
371: } catch (Exception e) {
372: throw new SerializerException(
373: SerializerException.FILE_WRITER_ERROR
374: .create(new String[] { filename,
375: e.getMessage() }));
376: }
377: }
378:
379: /**
380: * returns the key for a particular process setting. False if the key
381: * doesn't exist.
382: *
383: * @param key
384: * @return
385: */
386: public final boolean getSetting(String key) {
387: Object o = processSettings.get(key);
388: if ((o == null) || (!(o instanceof Boolean)))
389: return false;
390: return ((Boolean) o).booleanValue();
391: }
392:
393: /**
394: * set a process setting for a given key
395: *
396: * @param key
397: * instruction to set
398: * @param value
399: * true or false
400: */
401: protected final void setSetting(String key, boolean value) {
402: processSettings
403: .put(key, (value ? Boolean.TRUE : Boolean.FALSE));
404: }
405:
406: /**
407: * set instruction flags to new settings
408: *
409: * @param settings
410: */
411: protected final void checkSettings(Map settings) {
412: /** ensure we don't have settings from a previous run */
413: resetSettings();
414: /** process the new isntructionSet */
415: if ((settings == null) || (settings.size() == 0))
416: return;
417: Iterator _it = settings.keySet().iterator();
418: while (_it.hasNext()) {
419: try {
420: String key = (String) _it.next();
421: Object o = settings.get(key);
422: if ((o != null) && (o instanceof Boolean))
423: setSetting(key, ((Boolean) o).booleanValue());
424: } catch (Exception e) {
425: log.error("checkSettings", e);
426: }
427: }
428: }
429:
430: /**
431: * On import, get the basic SnapShot data
432: *
433: */
434: protected void getSnapshotData() {
435: logMe("date created : " + getSnapshot().getDateCreated());
436: logMe("software Version : " + getSnapshot().getSavedVersion());
437: logMe("software SUbVersion : "
438: + getSnapshot().getSavedSubversion());
439: }
440:
441: /**
442: * On export, set the basic SnapShot data
443: *
444: */
445: protected void setSnapshotData() {
446: java.util.Date d1 = new java.util.Date();
447: Date d = new Date(d1.getTime());
448: getSnapshot().setDateCreated(d.toString());
449: getSnapshot().setSavedVersion(
450: getSnapshot().getSoftwareVersion());
451: getSnapshot().setSavedSubversion(
452: getSnapshot().getSoftwareSubVersion());
453: }
454:
455: /**
456: * simple lookup for object from a map
457: * @param map
458: * @param _fullPath
459: * @return
460: */
461: protected final Object getObjectBehindPath(Map map, String _fullPath) {
462: return map.get(_fullPath);
463: }
464:
465: /**
466: * ++++++++++++++++++++++++++++++HELPERS
467: * +++++++++++++++++++++++++++++++++++++++++++++
468: */
469:
470: /**
471: * remove a given sequence from the beginning of a string
472: */
473: protected final String removeFromString(String base, String excess) {
474: return base.replaceFirst(excess, "").trim();
475: }
476:
477: /**
478: *
479: * just a Simple helper to make code more readable
480: *
481: * @param text
482: */
483: protected final void logMe(String text) {
484: if (log.isDebugEnabled())
485: log.debug(text);
486: }
487:
488: /**
489: * Helper routine to create a unique filename for a backup of an existing
490: * filename....not intended to be rocket science...
491: *
492: * @param name
493: * @return
494: */
495: protected final String createUniqueBackupFilename(String name) {
496: String newName = name + ".bak";
497:
498: File f = new File(newName);
499: int counter = 0;
500: if (!(f.exists()))
501: return newName;
502: while (counter < 100) {
503: String newName1 = newName + counter;
504: if (!(new File(newName1).exists()))
505: return newName1;
506: counter++;
507: }
508: return null;
509: }
510:
511: /**
512: * convert a list of elements in a string, seperated by ',' into an arraylist of strings
513: * @param _line Strinbg containing one or more elements seperated by ','
514: * @return list of elements of null
515: */
516: protected final ArrayList getTokens(String _line) {
517: if ((_line == null) || (_line.length() == 0))
518: return null;
519:
520: StringTokenizer st = new StringTokenizer(_line, ",");
521: ArrayList list = new ArrayList();
522:
523: while (st.hasMoreTokens())
524: list.add(st.nextToken());
525: return list;
526: }
527:
528: protected final String recreatePassword(char[] savedPassword) {
529: if (savedPassword == null)
530: return null;
531: return new String(savedPassword);
532: }
533:
534: /**
535: * reset instruction flags to default settings (all true)
536: *
537: */
538: protected abstract void resetSettings();
539:
540: /**
541: * The workhorse for exporting data
542: *
543: * @param binding
544: * established XML binding
545: * @return
546: * @throws SerializerException
547: */
548: protected abstract void processExport(String name,
549: XMLBinding binding) throws SerializerException;
550:
551: /**
552: * The workhorse for importing data
553: *
554: * @param binding
555: * established XML binding
556: * @return
557: * @throws SerializerException
558: */
559: protected abstract void processImport() throws SerializerException;
560:
561: /**
562: * Setup the binding for the different classes, mapping each extracted class
563: * to a unique tag name in the XML
564: *
565: * @param binding
566: */
567: protected abstract void setupAliases(XMLBinding binding);
568:
569: /**
570: * return the class for the serializer data , for example JSSeedData.class)
571: *
572: * @return
573: */
574: protected abstract Class getSerializerDataClass();
575:
576: /**
577: * return the XML tag for the serializer data , for example "JSSnapShot")
578: *
579: * @return
580: */
581: protected abstract String getSerializerDataTag();
582:
583: public JSSnapshot getSnapshot() {
584: return snapshot;
585: }
586:
587: public void setSnapshot(JSSnapshot snapshot) {
588: this.snapshot = snapshot;
589: }
590:
591: }
|