001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.reporter;
006:
007: import org.apache.xmlbeans.XmlException;
008:
009: import com.tc.config.Loader;
010: import com.tc.config.schema.dynamic.ParameterSubstituter;
011: import com.tc.sysinfo.EnvStats;
012: import com.tc.util.ArchiveBuilder;
013: import com.tc.util.ZipBuilder;
014: import com.terracottatech.config.Client;
015: import com.terracottatech.config.Server;
016: import com.terracottatech.config.Servers;
017: import com.terracottatech.config.TcConfigDocument.TcConfig;
018:
019: import java.io.File;
020: import java.io.IOException;
021: import java.text.DateFormat;
022: import java.text.SimpleDateFormat;
023: import java.util.Date;
024: import java.util.HashSet;
025: import java.util.Set;
026:
027: import javax.xml.namespace.QName;
028:
029: /**
030: * This utility is used to archive Terracotta execution environment information for debugging purposes. Run the
031: * <tt>main()</tt> with no arguments for usage.
032: */
033: public final class ArchiveUtil {
034:
035: private final boolean isFull;
036: private final boolean isClient;
037: private final File tcConfig;
038: private final File archiveFile;
039: private static final String STDOUT = "stdout:";
040: private static final String STDERR = "stderr:";
041: private static final String ARCHIVE_FILE_NAME = "tc-archive";
042: private static final String INVALID = "Invalid Arguments:\n\n";
043: private static final String DASH_N = "-n";
044: private static final String DASH_C = "-c";
045: private static final String USAGE = "** Terracotta Archive Tool **\n\n"
046: + "A utility for archiving Terracotta environment information.\n\n"
047: + "\tValid Arguments are:\n\n\t["
048: + DASH_N
049: + "] (No Data - excludes data files)\n\t["
050: + DASH_C
051: + "] (Client - include files from the dso client)"
052: + "\n\t<path to terracotta config xml file (tc-config.xml)>"
053: + " | <path to data and/or logs directory>"
054: + "\n\t[<output filename in .zip format>]\n\nExamples:\n\n\t"
055: + "# java "
056: + ArchiveUtil.class.getName()
057: + " tc-config.xml /home/someuser/tc-archive_server.zip"
058: + "\n\tor\n\t# java "
059: + ArchiveUtil.class.getName()
060: + " /export1/terracotta/server-logs"
061: + "\n\nUsage Summary:\n\n\tTypically you will use this tool to create a full "
062: + "archive of the Terracotta server instance.\n\t"
063: + "You may also want to create archives on the DSO client machines using"
064: + " the -c option. There are two\n\tscenarios where you may "
065: + "need to use the directory location instead of the config file path."
066: + "\n\n\t\t1. The DSO client may not have a local copy of the tc-config.xml"
067: + "\n\t\t2. The tc-config.xml logs and data elements may contain wildcards"
068: + " which use timestamps or \n\t\t environment variables which cannot be"
069: + " resolved.\n\nNotes:\n\n\tThe execution command may vary:"
070: + "\n\t\t# ./archive-util ...\n\n\tSpecifying a directory location as the"
071: + " first command will recursively archive it's entire contents";
072:
073: private static final Set validDashArgs = new HashSet();
074: static {
075: validDashArgs.add(DASH_N);
076: validDashArgs.add(DASH_C);
077: }
078:
079: private ArchiveUtil(boolean isFull, boolean isClient,
080: File archivePath, File fileName) {
081: this .isFull = isFull;
082: this .isClient = isClient;
083: this .tcConfig = archivePath;
084: if (fileName == null) {
085: File userDir = new File(System.getProperty("user.dir"));
086: if (!userDir.exists())
087: throw new RuntimeException(
088: "Unexpected error - system property user.dir does not resolve to an actual directory: "
089: + userDir);
090: DateFormat df = new SimpleDateFormat("y-M-d");
091: String name = ARCHIVE_FILE_NAME + "_"
092: + df.format(new Date(System.currentTimeMillis()))
093: + ".zip";
094: this .archiveFile = new File(userDir + File.separator + name);
095: } else {
096: this .archiveFile = fileName;
097: }
098: }
099:
100: private static void quit(String msg) {
101: System.err.println(msg);
102: System.exit(0);
103: }
104:
105: private static void escape(String msg, Exception e) {
106: System.out.println(INVALID + msg);
107: if (e != null)
108: e.printStackTrace();
109: System.exit(0);
110: }
111:
112: public static void main(String[] args) {
113: if (args.length < 1)
114: escape(USAGE, null);
115: boolean dashArgs = true;
116: int locationCmd = -1;
117: int fileArg = -1;
118: Set dashSet = new HashSet(2);
119: for (int i = 0; i < args.length; i++) {
120: if (args[i].startsWith("-")) {
121: if (!dashArgs)
122: escape(USAGE, null);
123: if (validDashArgs.contains(args[i]))
124: dashSet.add(args[i]);
125: else
126: escape(USAGE, null);
127: } else {
128: dashArgs = false;
129: if (fileArg + locationCmd > 1)
130: escape(USAGE, null);
131: if (locationCmd < 0)
132: locationCmd = i;
133: else if (fileArg < 0)
134: fileArg = i;
135: if (fileArg + locationCmd == -2)
136: escape(USAGE, null);
137: }
138: }
139: if (dashSet.size() > 2)
140: escape(USAGE, null);
141: boolean dashC = dashSet.contains(DASH_C);
142: boolean dashN = dashSet.contains(DASH_N);
143:
144: if (locationCmd < 0)
145: escape(
146: "Please specify the Terracotta config file location or logs/data directory location\n\n"
147: + USAGE, null);
148: File tcConfigFile = new File(args[locationCmd]);
149: if (!tcConfigFile.exists())
150: escape("\tTerracotta Configuration file: " + tcConfigFile
151: + "\n\tdoes not exist\n\n" + USAGE, null);
152: File outputFile = null;
153: if (fileArg > 0) {
154: outputFile = new File(new File(args[fileArg])
155: .getAbsolutePath());
156: if (!new File(outputFile.getParent()).exists())
157: escape(
158: "\tThe directory specified for the output file does not exist",
159: null);
160: }
161: try {
162: new ArchiveUtil(!dashN, dashC, tcConfigFile, outputFile)
163: .createArchive();
164: } catch (IOException e) {
165: escape("\tUnable to read Terracotta configuration file\n",
166: e);
167: } catch (XmlException e) {
168: escape("\tUnable to parse Terracotta configuration file\n",
169: e);
170: }
171: }
172:
173: private File makeAbsolute(File file) {
174: if (file.isAbsolute())
175: return file;
176: return new File(tcConfig.getParent() + File.separator + file);
177: }
178:
179: private File getClientLogsLocation(TcConfig configBeans)
180: throws IOException, XmlException {
181: Client clients = configBeans.getClients();
182: if (clients == null)
183: quit("The Terracotta config specified doesn't contain the <clients> element.\nYou may have provided a server config by mistake.");
184: String logs = clients.getLogs();
185: if (isStdX(logs))
186: return null;
187: if (logs == null)
188: logs = Client.type
189: .getElementProperty(QName.valueOf("logs"))
190: .getDefaultText();
191: String clientLogs = ParameterSubstituter.substitute(logs);
192: File clientLogsDir = makeAbsolute(new File(clientLogs));
193: if (!clientLogsDir.exists())
194: quit("\nError occured while parsing: " + tcConfig
195: + "\n\tUnable to locate client log files at: "
196: + clientLogs);
197: return clientLogsDir;
198: }
199:
200: private boolean isStdX(String value) {
201: if (value == null)
202: return false;
203: return (value.equals(STDOUT) || value.equals(STDERR));
204: }
205:
206: private Server[] getServersElement(TcConfig configBeans)
207: throws IOException, XmlException {
208: Servers servers = configBeans.getServers();
209: if (servers == null)
210: quit("The Terracotta config specified doesn't contain the <servers> element");
211: return servers.getServerArray();
212: }
213:
214: private File[] getServerLogsLocation(TcConfig configBeans)
215: throws IOException, XmlException {
216: Server[] servers = getServersElement(configBeans);
217: String[] logs = new String[servers.length];
218: File[] logFiles = new File[servers.length];
219: for (int i = 0; i < servers.length; i++) {
220: logs[i] = servers[i].getLogs();
221: if (isStdX(logs[i]))
222: logs[i] = null;
223: if (logs[i] == null)
224: logs[i] = Server.type.getElementProperty(
225: QName.valueOf("logs")).getDefaultText();
226: logs[i] = ParameterSubstituter.substitute(logs[i]);
227: File serverLogsDir = makeAbsolute(new File(logs[i]));
228: if (!serverLogsDir.exists())
229: quit("\nError occured while parsing: "
230: + tcConfig
231: + "\n\tUnable to resolve the server log location element to an actual file: "
232: + logs[i]);
233: logFiles[i] = serverLogsDir;
234: }
235: return logFiles;
236: }
237:
238: private File[] getServerDataLocation(TcConfig configBeans)
239: throws IOException, XmlException {
240: if (!isFull)
241: return null;
242: Server[] servers = getServersElement(configBeans);
243: String[] serverData = new String[servers.length];
244: File[] dataFiles = new File[servers.length];
245: for (int i = 0; i < servers.length; i++) {
246: serverData[i] = servers[i].getData();
247: if (serverData[i] == null)
248: serverData[i] = Server.type.getElementProperty(
249: QName.valueOf("data")).getDefaultText();
250: serverData[i] = ParameterSubstituter
251: .substitute(serverData[i]);
252: File serverDataDir = makeAbsolute(new File(serverData[i]));
253: if (!serverDataDir.exists())
254: quit("\nError occured while parsing: "
255: + tcConfig
256: + "\n\tUnable to resolve the server data location element to an actual file: "
257: + serverData[i]);
258: dataFiles[i] = serverDataDir;
259: }
260: return dataFiles;
261: }
262:
263: private void createPathArchive() {
264: try {
265: System.out
266: .println("Archiving:\n----------------------------------------");
267: ArchiveBuilder zip = new ZipBuilder(archiveFile, true);
268: zip.putEntry("env-stats", EnvStats.report().getBytes());
269: zip.putTraverseDirectory(tcConfig, tcConfig.getName());
270: zip.finish();
271: } catch (IOException e) {
272: System.out
273: .println("Unexpected error - unable to write Terracotta archive: "
274: + archiveFile);
275: e.printStackTrace();
276: System.exit(1);
277: }
278: System.out.println("\n\nWrote archive to:" + archiveFile);
279: }
280:
281: private void createArchive() throws IOException, XmlException {
282: if (tcConfig.isDirectory()) {
283: createPathArchive();
284: return;
285: }
286: TcConfig configBeans = new Loader().parse(tcConfig)
287: .getTcConfig();
288: File clientLogsDir = null;
289: File[] serverLogsDir = null;
290: File[] serverDataDir = null;
291: if (isClient) {
292: clientLogsDir = getClientLogsLocation(configBeans);
293: } else {
294: serverLogsDir = getServerLogsLocation(configBeans);
295: serverDataDir = getServerDataLocation(configBeans);
296: }
297: try {
298: ArchiveBuilder zip = new ZipBuilder(archiveFile, true);
299: System.out.println("Archiving:");
300: zip.putEntry(tcConfig.getName(), zip.readFile(tcConfig));
301: if (isClient) {
302: if (clientLogsDir != null)
303: zip.putTraverseDirectory(clientLogsDir,
304: clientLogsDir.getName());
305: } else {
306: for (int i = 0; i < serverLogsDir.length; i++) {
307: if (serverLogsDir[i] != null)
308: zip.putTraverseDirectory(serverLogsDir[i],
309: serverLogsDir[i].getName());
310: }
311: if (serverDataDir != null) {
312: for (int i = 0; i < serverDataDir.length; i++) {
313: zip.putTraverseDirectory(serverDataDir[i],
314: serverDataDir[i].getName());
315: }
316: }
317: }
318: zip.finish();
319: } catch (IOException e) {
320: System.out
321: .println("Unexpected error - unable to write Terracotta archive: "
322: + archiveFile);
323: e.printStackTrace();
324: System.exit(1);
325: }
326: System.out.println("\n\nWrote archive to:" + archiveFile);
327: }
328: }
|