001/*
002 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     bstefanescu
016 */
017package org.nuxeo.shell.automation.cmds;
018
019import java.io.File;
020import java.io.InputStream;
021import java.text.SimpleDateFormat;
022import java.util.Date;
023
024import org.codehaus.jackson.JsonNode;
025import org.codehaus.jackson.map.ObjectMapper;
026import org.nuxeo.ecm.automation.client.OperationRequest;
027import org.nuxeo.ecm.automation.client.model.Blob;
028import org.nuxeo.ecm.automation.client.model.FileBlob;
029import org.nuxeo.shell.Argument;
030import org.nuxeo.shell.Command;
031import org.nuxeo.shell.Context;
032import org.nuxeo.shell.Parameter;
033import org.nuxeo.shell.ShellConsole;
034import org.nuxeo.shell.ShellException;
035import org.nuxeo.shell.automation.RemoteContext;
036import org.nuxeo.shell.fs.FileCompletor;
037import org.nuxeo.shell.fs.FileSystem;
038import org.nuxeo.shell.utils.StringUtils;
039
040/**
041 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
042 */
043@Command(name = "audit", help = "Run a query against audit service")
044public class Audit implements Runnable {
045
046    @Context
047    protected RemoteContext ctx;
048
049    @Parameter(name = "-ctx", hasValue = true, help = "Use this to set query variables. Syntax is: \"k1=v1,k1=v2\"")
050    protected String queryVars;
051
052    @Parameter(name = "-s", hasValue = true, help = "Use this to change the separator used in query variables. THe default is ','")
053    protected String sep = ",";
054
055    @Parameter(name = "-f", hasValue = true, completor = FileCompletor.class, help = "Use this to save results in a file. Otherwise results are printed on the screen.")
056    protected File file;
057
058    @Parameter(name = "-max", hasValue = true, help = "The max number of rows to return.")
059    protected int max;
060
061    @Parameter(name = "-page", hasValue = true, help = "The current page to return. To be used in conjunction with -max.")
062    protected int page = 1;
063
064    @Argument(name = "query", index = 0, required = true, help = "The query to run. Query is in JPQL format")
065    protected String query;
066
067    public void run() {
068        try {
069            OperationRequest req = ctx.getSession().newRequest("Audit.Query").set("query", query).set("maxResults", max).set(
070                    "pageNo", page);
071            if (queryVars != null) {
072                for (String pair : queryVars.split(sep)) {
073                    String[] ar = StringUtils.split(pair, '=', true);
074                    req.setContextProperty("audit.query." + ar[0], ar[1]);
075                }
076            }
077            Blob blob = (Blob) req.execute();
078            String content = null;
079            if (file != null) {
080                ((FileBlob) blob).getFile().renameTo(file);
081            } else {
082                InputStream in = blob.getStream();
083                try {
084                    content = FileSystem.readContent(in);
085                } finally {
086                    in.close();
087                    ((FileBlob) blob).getFile().delete();
088                }
089                print(ctx.getShell().getConsole(), content);
090            }
091        } catch (Exception e) {
092            throw new ShellException("Failed to query audit.", e);
093        }
094    }
095
096    private final void printString(ShellConsole console, JsonNode obj, String key) {
097        JsonNode v = obj.get(key);
098        if (v != null) {
099            String s = v.getTextValue();
100            if (s != null) {
101                console.print(v.getTextValue());
102                return;
103            }
104        }
105        console.print("[null]");
106    }
107
108    private final void printDate(ShellConsole console, JsonNode obj, String key, SimpleDateFormat fmt) {
109        JsonNode v = obj.get(key);
110        if (v != null) {
111            console.print(fmt.format(new Date(v.getValueAsLong())));
112        } else {
113            console.print("[null]");
114        }
115    }
116
117    protected void print(ShellConsole console, String content) throws Exception {
118        ObjectMapper mapper = new ObjectMapper();
119        JsonNode rows = mapper.readTree(content);
120        if (!rows.isArray()) {
121            console.print("Invalid JSON object received:\n" + content);
122            return;
123        }
124        int len = rows.size();
125        SimpleDateFormat fmt = new SimpleDateFormat();
126        for (int i = 0; i < len; i++) {
127            JsonNode obj = (JsonNode) rows.get(i);
128            printString(console, obj, "eventId");
129            console.print("\t");
130            printString(console, obj, "category");
131            console.print("\t");
132            printDate(console, obj, "eventDate", fmt);
133            console.print("\t");
134            printString(console, obj, "principal");
135            console.print("\t");
136            printString(console, obj, "docUUID");
137            console.print("\t");
138            printString(console, obj, "docType");
139            console.print("\t");
140            printString(console, obj, "docLifeCycle");
141            console.print("\t");
142            printString(console, obj, "comment");
143            console.println();
144        }
145    }
146}