/*
* (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* bstefanescu
* vpasquier <vpasquier@nuxeo.com>
*/
package org.nuxeo.ecm.automation.server.jaxrs.doc;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.automation.AutomationService;
import org.nuxeo.ecm.automation.OperationDocumentation;
import org.nuxeo.ecm.automation.OperationException;
import org.nuxeo.ecm.automation.core.trace.Trace;
import org.nuxeo.ecm.automation.core.trace.TracerFactory;
import org.nuxeo.ecm.automation.io.yaml.YamlWriter;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.webengine.JsonFactoryManager;
import org.nuxeo.ecm.webengine.WebEngine;
import org.nuxeo.ecm.webengine.WebException;
import org.nuxeo.ecm.webengine.jaxrs.context.RequestContext;
import org.nuxeo.ecm.webengine.jaxrs.session.SessionFactory;
import org.nuxeo.ecm.webengine.model.Template;
import org.nuxeo.ecm.webengine.model.WebObject;
import org.nuxeo.ecm.webengine.model.impl.AbstractResource;
import org.nuxeo.ecm.webengine.model.impl.ResourceTypeImpl;
import org.nuxeo.runtime.api.Framework;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
@WebObject(type = "doc")
@Produces("text/html;charset=UTF-8")
public class DocResource extends AbstractResource<ResourceTypeImpl> {
private final Log log = LogFactory.getLog(DocResource.class);
protected AutomationService service;
protected List<OperationDocumentation> ops;
@Override
public void initialize(Object... args) {
try {
service = Framework.getLocalService(AutomationService.class);
ops = service.getDocumentation();
} catch (OperationException e) {
log.error("Failed to get automation service", e);
throw WebException.wrap(e);
}
}
protected Template getTemplateFor(String browse) {
return getTemplateView("index").arg("browse", browse);
}
protected Template getTemplateView(String name) {
Map<String, List<OperationDocumentation>> cats = new HashMap<String, List<OperationDocumentation>>();
for (OperationDocumentation op : ops) {
List<OperationDocumentation> list = cats.get(op.getCategory());
if (list == null) {
list = new ArrayList<OperationDocumentation>();
cats.put(op.getCategory(), list);
}
list.add(op);
}
// sort categories
List<String> catNames = new ArrayList<>();
catNames.addAll(cats.keySet());
Collections.sort(catNames);
Map<String, List<OperationDocumentation>> scats = new LinkedHashMap<String, List<OperationDocumentation>>();
for (String catName : catNames) {
scats.put(catName, cats.get(catName));
}
return getView(name).arg("categories", scats).arg("operations", ops);
}
@GET
public Object doGet(@QueryParam("id") String id, @QueryParam("browse") String browse) {
if (id == null) {
return getTemplateFor(browse);
} else {
OperationDocumentation opDoc = null;
for (OperationDocumentation op : ops) {
if (op.getId().equals(id)) {
opDoc = op;
break;
}
}
if (opDoc == null) {
throw new WebApplicationException(Response.status(404).build());
}
Template tpl = getTemplateFor(browse);
tpl.arg("operation", opDoc);
CoreSession session = SessionFactory.getSession();
if (((NuxeoPrincipal) session.getPrincipal()).isAdministrator()) {
// add yaml format - chains are exposing their operations
// so this information should be restricted to administrators.
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
YamlWriter.toYaml(out, opDoc);
tpl.arg("yaml", out.toString());
} catch (IOException e) {
throw WebException.wrap(e);
}
}
return tpl;
}
}
protected boolean canManageTraces() {
return ((NuxeoPrincipal) WebEngine.getActiveContext().getPrincipal()).isAdministrator();
}
@GET
@Path("/wiki")
public Object doGetWiki() {
return getTemplateView("wiki");
}
public boolean isTraceEnabled() {
TracerFactory tracerFactory = Framework.getLocalService(TracerFactory.class);
return tracerFactory.getRecordingState();
}
@GET
@Path("/toggleTraces")
public Object toggleTraces() {
if (!canManageTraces()) {
return "You can not manage traces";
}
TracerFactory tracerFactory = Framework.getLocalService(TracerFactory.class);
tracerFactory.toggleRecording();
HttpServletRequest request = RequestContext.getActiveContext().getRequest();
String url = request.getHeader("Referer");
try {
return Response.seeOther(new URI(url)).build();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
@GET
@Path("/toggleStackDisplay")
@Produces("text/plain")
public Object toggleStackDisplay() {
if (!canManageTraces()) {
return "You can not manage json exception stack display";
}
JsonFactoryManager jsonFactoryManager = Framework.getLocalService(JsonFactoryManager.class);
return String.valueOf(jsonFactoryManager.toggleStackDisplay());
}
@GET
@Path("/traces")
@Produces("text/plain")
public String doGetTrace(@QueryParam("opId") String opId) {
if (!canManageTraces()) {
return "You can not manage traces";
}
TracerFactory tracerFactory = Framework.getLocalService(TracerFactory.class);
Trace trace = tracerFactory.getTrace(opId);
if (trace == null) {
return "no trace";
}
return tracerFactory.print(trace);
}
public String[] getInputs(OperationDocumentation op) {
if (op == null) {
throw new IllegalArgumentException("Operation must not be null");
}
if (op.signature == null || op.signature.length == 0) {
return new String[0];
}
String[] result = new String[op.signature.length / 2];
for (int i = 0, k = 0; i < op.signature.length; i += 2, k++) {
result[k] = op.signature[i];
}
return result;
}
public String[] getOutputs(OperationDocumentation op) {
if (op == null) {
throw new IllegalArgumentException("Operation must not be null");
}
if (op.signature == null || op.signature.length == 0) {
return new String[0];
}
String[] result = new String[op.signature.length / 2];
for (int i = 1, k = 0; i < op.signature.length; i += 2, k++) {
result[k] = op.signature[i];
}
return result;
}
public String getInputsAsString(OperationDocumentation op) {
String[] result = getInputs(op);
if (result == null || result.length == 0) {
return "void";
}
return StringUtils.join(result, ", ");
}
public String getOutputsAsString(OperationDocumentation op) {
String[] result = getOutputs(op);
if (result == null || result.length == 0) {
return "void";
}
return StringUtils.join(result, ", ");
}
public String getParamDefaultValue(OperationDocumentation.Param param) {
if (param.values != null && param.values.length > 0) {
return StringUtils.join(param.values, ", ");
}
return "";
}
public boolean hasOperation(OperationDocumentation op) {
if (op == null) {
throw new IllegalArgumentException("Operation must not be null");
}
if (op.getOperations() == null || op.getOperations().length == 0) {
return false;
}
return true;
}
}