001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdo;
012:
013: import com.versant.core.util.BeanUtils;
014: import com.versant.core.logging.LogEvent;
015: import com.versant.core.metric.MetricSnapshotPacket;
016:
017: import javax.jdo.JDOHelper;
018: import java.util.Properties;
019: import java.util.Date;
020: import java.io.*;
021: import java.text.SimpleDateFormat;
022:
023: import com.versant.core.common.BindingSupportImpl;
024: import com.versant.core.common.config.ConfigParser;
025:
026: /**
027: * Utility bean to download event logs and performance metric snapshots and
028: * store then in binary and/or text files. The binary files can be opened
029: * in the Workbench for later analysis. This can be run from the command
030: * line or embedded into an application. The JDO Genie server uses an
031: * instance of this class for its built in logging features. To use it in
032: * an application create an instance, set properties and invoke the run()
033: * method at regular intervals (e.g. create a Thread to do this).
034: */
035: public class LogDownloader implements VersantBackgroundTask {
036:
037: private String project = "versant.properties";
038: private String host;
039: private int port;
040: private String server;
041: private String username;
042: private String password;
043: private VersantPersistenceManagerFactory pmf;
044: private int eventPollSecs = 1;
045: private int metricPollSecs = 60;
046: private boolean append;
047: private int maxFileSizeK = 1000;
048: private int backups = 3;
049: private String filename;
050: private boolean eventBinary = true;
051: private boolean eventText;
052: private String dateFormat = "HH:mm:ss.SSS";
053: private boolean metricBinary = true;
054: private boolean quiet;
055: private boolean single;
056:
057: private boolean shutdown;
058:
059: private int lastEventId;
060: private RollingFile eventBinaryFile;
061: private ObjectOutputStream eventOutput;
062: private RollingFile eventTextFile;
063: private OutputStreamWriter eventWriter;
064: private SimpleDateFormat eventDateFormat;
065:
066: private int lastMetricId;
067: private RollingFile metricBinaryFile;
068: private ObjectOutputStream metricOutput;
069: private boolean metricsDone;
070:
071: private boolean downloadedEvents;
072: private boolean downloadedMetrics;
073:
074: private static final String LINE_SEPARATOR = System
075: .getProperty("line.separator");
076:
077: public static void main(String[] args) {
078: try {
079: LogDownloader l = new LogDownloader();
080: BeanUtils.setCommandLineArgs(l, args, new String[] {
081: "host", "port", "server", "username", "password",
082: "eventPollSecs", "metricPollSecs", "append",
083: "maxFileSizeK", "backups", "filename",
084: "eventBinary", "eventText", "metricBinary",
085: "quiet", "project" });
086: run(l);
087: System.exit(0);
088: } catch (Exception x) {
089: x.printStackTrace(System.out);
090: System.exit(1);
091: }
092: }
093:
094: public static void run(LogDownloader l) throws Exception {
095: if (l.getProject() != null) {
096: Properties p = loadProperties(l.getProject());
097: if (l.getHost() == null) {
098: l.setHost(p.getProperty("versant.host"));
099: }
100: if (l.getServer() == null) {
101: l.setServer(p.getProperty(ConfigParser.SERVER));
102: }
103: if (l.getPort() == 0) {
104: String s = p.getProperty(ConfigParser.SERVER_PORT);
105: if (s != null) {
106: try {
107: l.setPort(Integer.parseInt(s));
108: } catch (NumberFormatException e) {
109: throw BindingSupportImpl.getInstance()
110: .illegalArgument("Invalid port: " + e);
111: }
112: }
113: }
114: }
115: if (l.getHost() == null) {
116: throw BindingSupportImpl.getInstance().illegalArgument(
117: "-host is required");
118: }
119: if (l.getServer() == null) {
120: throw BindingSupportImpl.getInstance().illegalArgument(
121: "-server is required");
122: }
123: Properties p = new Properties();
124: String host = l.getHost();
125: int port = l.getPort();
126: String server = l.getServer();
127: p.setProperty(ConfigParser.PMF_CLASS,
128: "com.versant.core.jdo.BootstrapPMF");
129: p.setProperty("versant.host", host);
130: p.setProperty(ConfigParser.SERVER, server);
131: if (port != 0)
132: p.setProperty(ConfigParser.SERVER_PORT, Integer
133: .toString(port));
134: if (!l.isQuiet()) {
135: System.out.println("Connecting to " + host
136: + (port == 0 ? "" : ":" + port) + "/" + server);
137: }
138: if (l.getUsername() != null) {
139: p.setProperty("versant.remoteUsername", l.getUsername());
140: }
141: if (l.getPassword() != null) {
142: p.setProperty("versant.remotePassword", l.getPassword());
143: }
144: VersantPersistenceManagerFactory pmf = (VersantPersistenceManagerFactory) JDOHelper
145: .getPersistenceManagerFactory(p);
146: l.setPmf(pmf);
147: l.run();
148: }
149:
150: private static Properties loadProperties(String filename)
151: throws IOException {
152: ClassLoader cl = LogDownloader.class.getClassLoader();
153: InputStream in = null;
154: try {
155: if (filename.startsWith("/"))
156: filename = filename.substring(1);
157: in = cl.getResourceAsStream(filename);
158: if (in == null) {
159: throw BindingSupportImpl.getInstance().runtime(
160: "Resource not found: " + filename);
161: }
162: Properties p = new Properties();
163: p.load(in);
164: return p;
165: } finally {
166: if (in != null)
167: in.close();
168: }
169: }
170:
171: public void setPmf(VersantPersistenceManagerFactory pmf)
172: throws Exception {
173: this .pmf = pmf;
174:
175: if (!eventBinary && !eventText && !metricBinary) {
176: return;
177: }
178:
179: if (filename == null) {
180: filename = generateFilename();
181: }
182:
183: int maxSize = maxFileSizeK * 1024;
184:
185: if (eventBinary) {
186: eventBinaryFile = new RollingFile(filename + ".jdolog");
187: eventBinaryFile.setMaxBackupIndex(backups);
188: eventBinaryFile.setMaxSize(maxSize);
189: openEventBinaryFile();
190: }
191:
192: if (eventText) {
193: eventDateFormat = new SimpleDateFormat(dateFormat);
194: eventTextFile = new RollingFile(filename + ".txt");
195: eventTextFile.setMaxBackupIndex(backups);
196: eventTextFile.setMaxSize(maxSize);
197: eventTextFile.setAppend(append);
198: openEventTextFile();
199: }
200:
201: if (metricBinary) {
202: metricBinaryFile = new RollingFile(filename + ".jdoperf");
203: metricBinaryFile.setMaxBackupIndex(backups);
204: metricBinaryFile.setMaxSize(maxSize);
205: openMetricBinaryFile();
206: }
207: }
208:
209: /**
210: * Generate a filename. This is invoked if no filename has been set when
211: * the pmf is set.
212: */
213: protected String generateFilename() {
214: if (server != null) {
215: return "openaccess_" + (host == null ? "" : host + "_")
216: + server;
217: } else {
218: return "openaccess";
219: }
220: }
221:
222: public void run() {
223: try {
224: runImp();
225: } catch (Exception e) {
226: if (!shutdown) {
227: e.printStackTrace(System.out);
228: }
229: }
230: }
231:
232: protected void log(String msg) {
233: if (quiet)
234: return;
235: System.out.println(new Date() + " " + msg);
236: }
237:
238: protected void runImp() throws Exception {
239: boolean eventsOn = eventBinary || eventText;
240: if (!eventsOn && !metricBinary)
241: return;
242:
243: try {
244: log("Log downloader running");
245:
246: long lastEventTime = 0;
247: long lastMetricTime = 0;
248: int eventPoll = eventPollSecs * 1000;
249: if (eventPoll < 1000)
250: eventPoll = 1000;
251: int metricPoll = metricPollSecs * 1000;
252: if (metricPoll < 1000)
253: metricPoll = 1000;
254: long sleep = 0;
255: for (; !shutdown;) {
256: try {
257: if (sleep > 0)
258: Thread.sleep(sleep);
259: } catch (InterruptedException e) {
260: // ignore
261: }
262:
263: long now = System.currentTimeMillis();
264:
265: if (eventsOn) {
266: if (shutdown || now - lastEventTime >= eventPoll) {
267: lastEventTime = now;
268: downloadEvents();
269: sleep = eventPoll;
270: } else {
271: sleep = now - lastEventTime;
272: }
273: } else {
274: sleep = Long.MAX_VALUE;
275: }
276:
277: if (metricBinary) {
278: long nsleep;
279: if (shutdown || now - lastMetricTime >= metricPoll) {
280: lastMetricTime = now;
281: downloadMetrics();
282: nsleep = metricPoll;
283: } else {
284: nsleep = now - lastMetricTime;
285: }
286: if (nsleep < sleep)
287: sleep = nsleep;
288: }
289:
290: if (single && (downloadedEvents || downloadedMetrics)) {
291: shutdown();
292: }
293: }
294:
295: if (eventOutput != null)
296: eventOutput.close();
297: if (eventWriter != null)
298: eventWriter.close();
299: if (metricOutput != null)
300: metricOutput.close();
301: } finally {
302: log("Log downloader stopped");
303: }
304: }
305:
306: private void downloadEvents() throws IOException {
307: LogEvent[] a = pmf.getNewPerfEvents(lastEventId);
308: if (a == null)
309: return;
310: downloadedEvents = true;
311: int n = a.length;
312: log("Received " + n + " event(s)");
313: lastEventId = a[n - 1].getId();
314: if (eventText) {
315: StringBuffer s = new StringBuffer();
316: Date d = new Date();
317: for (int i = 0; i < n; i++) {
318: LogEvent ev = a[i];
319: d.setTime(ev.getStart());
320: s.append(eventDateFormat.format(d));
321: s.append(' ');
322: s.append(ev);
323: s.append(LINE_SEPARATOR);
324: }
325: eventWriter.write(s.toString());
326: if (eventTextFile.isRolloverRequired()) {
327: eventWriter.close();
328: eventTextFile.rollover();
329: openEventTextFile();
330: } else {
331: eventWriter.flush();
332: }
333: }
334: if (eventBinary) {
335: eventOutput.writeObject(a);
336: if (eventBinaryFile.isRolloverRequired()) {
337: eventOutput.close();
338: eventBinaryFile.rollover();
339: openEventBinaryFile();
340: } else {
341: eventOutput.flush();
342: }
343: }
344: }
345:
346: private void downloadMetrics() throws IOException {
347: // make sure each file contains the Metric's themselves
348: if (!metricsDone) {
349: metricOutput.writeObject(pmf.getMetrics());
350: metricOutput.flush();
351: metricsDone = true;
352: }
353: MetricSnapshotPacket a = pmf
354: .getNewMetricSnapshots(lastMetricId);
355: if (a == null)
356: return;
357: downloadedMetrics = true;
358: log("Received " + a.getSize() + " snapshot(s)");
359: lastMetricId = a.getMostRecentID();
360: metricOutput.writeObject(a);
361: if (metricBinaryFile.isRolloverRequired()) {
362: metricOutput.close();
363: metricBinaryFile.rollover();
364: openMetricBinaryFile();
365: metricsDone = false;
366: } else {
367: metricOutput.flush();
368: }
369: }
370:
371: private void openEventBinaryFile() throws IOException {
372: eventOutput = new ObjectOutputStream(eventBinaryFile.getOut());
373: }
374:
375: private void openEventTextFile() throws IOException {
376: eventWriter = new OutputStreamWriter(eventTextFile.getOut());
377: }
378:
379: private void openMetricBinaryFile() throws IOException {
380: metricOutput = new ObjectOutputStream(metricBinaryFile.getOut());
381: }
382:
383: /**
384: * Stop downloading events. This will cause one more call to the server
385: * to get any remaining data and then the run method will return. You
386: * should also interrupt the Thread that called run to speed exit of
387: * the run method.
388: */
389: public void shutdown() {
390: shutdown = true;
391: }
392:
393: public String getProject() {
394: return project;
395: }
396:
397: public void setProject(String project) {
398: this .project = project;
399: }
400:
401: public String getHost() {
402: return host;
403: }
404:
405: public void setHost(String host) {
406: this .host = host;
407: }
408:
409: public int getPort() {
410: return port;
411: }
412:
413: public void setPort(int port) {
414: this .port = port;
415: }
416:
417: public String getServer() {
418: return server;
419: }
420:
421: public void setServer(String server) {
422: this .server = server;
423: }
424:
425: public String getUsername() {
426: return username;
427: }
428:
429: public void setUsername(String username) {
430: this .username = username;
431: }
432:
433: public String getPassword() {
434: return password;
435: }
436:
437: public void setPassword(String password) {
438: this .password = password;
439: }
440:
441: public VersantPersistenceManagerFactory getPmf() {
442: return pmf;
443: }
444:
445: public int getEventPollSecs() {
446: return eventPollSecs;
447: }
448:
449: public void setEventPollSecs(int eventPollSecs) {
450: this .eventPollSecs = eventPollSecs;
451: }
452:
453: public int getMetricPollSecs() {
454: return metricPollSecs;
455: }
456:
457: public void setMetricPollSecs(int metricPollSecs) {
458: this .metricPollSecs = metricPollSecs;
459: }
460:
461: public boolean isAppend() {
462: return append;
463: }
464:
465: public void setAppend(boolean append) {
466: this .append = append;
467: }
468:
469: public int getMaxFileSizeK() {
470: return maxFileSizeK;
471: }
472:
473: public void setMaxFileSizeK(int maxFileSizeK) {
474: this .maxFileSizeK = maxFileSizeK;
475: }
476:
477: public int getBackups() {
478: return backups;
479: }
480:
481: public void setBackups(int backups) {
482: this .backups = backups;
483: }
484:
485: public String getFilename() {
486: return filename;
487: }
488:
489: public void setFilename(String filename) {
490: this .filename = filename;
491: }
492:
493: public boolean isEventBinary() {
494: return eventBinary;
495: }
496:
497: public void setEventBinary(boolean eventBinary) {
498: this .eventBinary = eventBinary;
499: }
500:
501: public boolean isEventText() {
502: return eventText;
503: }
504:
505: public void setEventText(boolean eventText) {
506: this .eventText = eventText;
507: }
508:
509: public boolean isMetricBinary() {
510: return metricBinary;
511: }
512:
513: public void setMetricBinary(boolean metricBinary) {
514: this .metricBinary = metricBinary;
515: }
516:
517: public boolean isQuiet() {
518: return quiet;
519: }
520:
521: public void setQuiet(boolean quiet) {
522: this .quiet = quiet;
523: }
524:
525: public String getDateFormat() {
526: return dateFormat;
527: }
528:
529: public void setDateFormat(String dateFormat) {
530: this .dateFormat = dateFormat;
531: }
532:
533: public boolean isSingle() {
534: return single;
535: }
536:
537: public void setSingle(boolean single) {
538: this.single = single;
539: }
540:
541: }
|