1 package org.xmlBlaster.util;
2
3 import java.io.File;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.util.Properties;
7 import java.util.logging.Logger;
8
9 import org.xmlBlaster.util.def.Constants;
10 import org.xmlBlaster.util.def.ErrorCode;
11 import org.xmlBlaster.util.key.KeyData;
12 import org.xmlBlaster.util.qos.QosData;
13
14 /**
15 * Dump message to file.
16 * The XML is in XmlScript format and can be refed.
17 * <pre>
18 * Configuration:
19 * xmlBlaster/FileDumper/directoryName -> ${user.home}/FileDumper
20 * xmlBlaster/FileDumper/forceBase64 -> false
21 * </pre>
22 * @author <a href="mailto:xmlBlaster@marcelruff.info">Marcel Ruff</a>
23 */
24 public class FileDumper {
25 private static String ME = "FileDumper";
26 private static Logger log = Logger.getLogger(FileDumper.class.getName());
27 private Global glob;
28 private String directoryName;
29 /** forceBase64==false: ASCII dump for content if possible (XML embedable) */
30 private boolean forceBase64 = false;
31
32 public FileDumper(Global glob) throws XmlBlasterException {
33 this(glob, null, glob.getProperty().get("xmlBlaster/FileDumper/forceBase64", false));
34 }
35
36 public FileDumper(Global glob, String dirName, boolean forceBase64) throws XmlBlasterException {
37 this.glob = glob;
38 this.forceBase64 = forceBase64;
39 if (dirName == null) {
40 this.directoryName = glob.getProperty().get("xmlBlaster/FileDumper/directoryName", System.getProperty("user.home") + System.getProperty("file.separator") + "FileDumper");
41 log.info("Created FileDumper instance forceBase64=" + this.forceBase64 + " to dump messages to directory xmlBlaster/FileDumper/directoryName=" + this.directoryName);
42 //log.info("Dumping occurrences of topic '" + Constants.OID_DEAD_LETTER + "' forceBase64=" + this.forceBase64 + " to directory " + this.directoryName);
43 }
44 else {
45 this.directoryName = dirName;
46 log.info("Created FileDumper instance forceBase64=" + this.forceBase64 + " to dump messages to directory " + this.directoryName);
47 }
48 initDirectory(null, "directoryName", this.directoryName);
49 }
50
51 public String dumpMessage(KeyData keyData, byte[] content, QosData qosData) {
52 return dumpMessage(keyData, content, qosData, true);
53 }
54
55 /**
56 * Dump dead message to hard disk. The file name is the receive timestamp of
57 * the message, for example
58 * <tt>/home/xmlblast/tmp/2004-10-23_18_52_39_87.xml</tt>
59 *
60 * @param qosData
61 * may not be null
62 * @return fileName
63 */
64 public String dumpMessage(KeyData keyData, byte[] content, QosData qosData, boolean verbose) {
65 String fnStr = "";
66 try {
67 if (content == null)
68 content = new byte[0];
69 String fn = (qosData == null) ? new Timestamp().toString() : qosData.getRcvTimestampNotNull().toString();
70 String key = (keyData == null) ? "" : keyData.toXml();
71 Properties props = new Properties();
72 if (!forceBase64)
73 props.put(Constants.TOXML_FORCEREADABLE, ""+true);
74 String qos = (qosData == null) ? "" : qosData.toXml("", props);
75 String oid = (keyData == null) ? "" : keyData.getOid();
76
77 fn = Global.getStrippedString(fn); // Strip chars like ":" so that fn is usable as a file name
78 fn = fn + ".xml";
79
80 initDirectory(null, "directoryName", this.directoryName); // In case somebody has removed it
81 File to_file = new File(this.directoryName, fn);
82
83 fnStr = to_file.getAbsolutePath();
84
85 FileOutputStream to = new FileOutputStream(to_file);
86 if (verbose)
87 log.info("Dumping message to '" + to_file.toString() + "'" );
88
89 StringBuffer sb = new StringBuffer(qos.length() + key.length() + 1024);
90 //sb.append("<?xml version='1.0' encoding='iso-8859-1'?>");
91 //sb.append("<?xml version='1.0' encoding='utf-8' ?>");
92
93 sb.append("\n <!-- Dump of topic '").append(oid).append("' -->");
94 sb.append("\n<xmlBlaster>");
95 sb.append("\n <publish>");
96 to.write(sb.toString().getBytes());
97 sb.setLength(0);
98
99 {
100 sb.append(qos);
101 sb.append(key);
102 to.write(sb.toString().getBytes());
103 sb.setLength(0);
104
105 // TODO: Potential charset problem when not Base64 protected
106 boolean doEncode = forceBase64;
107 if (!forceBase64) {
108 int len = content.length - 2;
109 for (int i=0; i<len; i++) {
110 if (content[i] == (byte)']' && content[i+1] == (byte)']' && content[i+2] == (byte)'>') {
111 doEncode = true;
112 break;
113 }
114 }
115 }
116
117 if (doEncode) {
118 EncodableData data = new EncodableData("content", null, Constants.TYPE_BLOB, Constants.ENCODING_BASE64);
119 data.setValue(content);
120 data.setSize(content.length);
121 to.write(data.toXml(" ").getBytes());
122 }
123 else {
124 EncodableData data = new EncodableData("content", null, null, null);
125 //String charSet = "UTF-8"; // "ISO-8859-1", "US-ASCII"
126 //data.setValue(new String(content, charSet), null);
127 data.setValueRaw(new String(content));
128 data.forceCdata(true);
129 data.setSize(content.length);
130 to.write(data.toXml(" ").getBytes());
131 }
132 }
133 {
134 //MsgUnitRaw msg = new MsgUnitRaw(key, content, qos);
135 //msg.toXml(" ", to);
136 }
137
138 sb.append("\n </publish>");
139 sb.append("\n</xmlBlaster>");
140 to.write(sb.toString().getBytes());
141 to.close();
142 }
143 catch (Throwable e) {
144 log.severe("Dumping of message failed: " + (qosData == null ? "" : qosData.toXml())
145 + (keyData == null ? "" : keyData.toXml()) + new String(content));
146 }
147 return fnStr;
148 }
149
150 /**
151 * Returns the specified directory or null or if needed it will create one
152 * @param parent
153 * @param propName For logging only
154 * @param dirName
155 * @return
156 * @throws XmlBlasterException
157 */
158 private File initDirectory(File parent, String propName, String dirName) throws XmlBlasterException {
159 File dir = null;
160 if (dirName != null) {
161 File tmp = new File(dirName);
162 if (tmp.isAbsolute() || parent == null) {
163 dir = new File(dirName);
164 }
165 else {
166 dir = new File(parent, dirName);
167 }
168 if (!dir.exists()) {
169 String absDirName = null;
170 try {
171 absDirName = dir.getCanonicalPath();
172 }
173 catch (IOException ex) {
174 absDirName = dir.getAbsolutePath();
175 }
176 log.info("Constructor: directory '" + absDirName + "' does not yet exist. I will create it");
177 boolean ret = dir.mkdir();
178 if (!ret)
179 throw new XmlBlasterException(this.glob, ErrorCode.RESOURCE_FILEIO, ME, "could not create directory '" + absDirName + "'");
180 }
181 if (!dir.isDirectory()) {
182 throw new XmlBlasterException(this.glob, ErrorCode.RESOURCE_FILEIO, ME, "'" + dir.getAbsolutePath() + "' is not a directory");
183 }
184 if (!dir.canRead())
185 throw new XmlBlasterException(this.glob, ErrorCode.RESOURCE_FILEIO, ME + ".constructor", "no rights to read from the directory '" + dir.getAbsolutePath() + "'");
186 if (!dir.canWrite())
187 throw new XmlBlasterException(this.glob, ErrorCode.RESOURCE_FILEIO, ME + ".constructor", "no rights to write to the directory '" + dir.getAbsolutePath() + "'");
188 }
189 else {
190 log.info("Constructor: the '" + propName + "' property is not set. Instead of moving concerned entries they will be deleted");
191 }
192 return dir;
193 }
194
195 public boolean isForceBase64() {
196 return forceBase64;
197 }
198
199 public void setForceBase64(boolean forceBase64) {
200 this.forceBase64 = forceBase64;
201 }
202
203 }
syntax highlighted by Code2HTML, v. 0.9.1