001/* 002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 * vpasquier <vpasquier@nuxeo.com> 019 */ 020package org.nuxeo.ecm.automation.server.jaxrs.doc; 021 022import java.io.ByteArrayOutputStream; 023import java.io.IOException; 024import java.net.URI; 025import java.net.URISyntaxException; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.LinkedHashMap; 030import java.util.List; 031import java.util.Map; 032 033import javax.servlet.http.HttpServletRequest; 034import javax.ws.rs.GET; 035import javax.ws.rs.Path; 036import javax.ws.rs.Produces; 037import javax.ws.rs.QueryParam; 038import javax.ws.rs.WebApplicationException; 039import javax.ws.rs.core.Response; 040 041import org.apache.commons.lang.StringUtils; 042import org.apache.commons.logging.Log; 043import org.apache.commons.logging.LogFactory; 044import org.nuxeo.ecm.automation.AutomationService; 045import org.nuxeo.ecm.automation.OperationDocumentation; 046import org.nuxeo.ecm.automation.OperationException; 047import org.nuxeo.ecm.automation.core.trace.Trace; 048import org.nuxeo.ecm.automation.core.trace.TracerFactory; 049import org.nuxeo.ecm.automation.io.yaml.YamlWriter; 050import org.nuxeo.ecm.core.api.CoreSession; 051import org.nuxeo.ecm.core.api.NuxeoPrincipal; 052import org.nuxeo.ecm.webengine.JsonFactoryManager; 053import org.nuxeo.ecm.webengine.WebEngine; 054import org.nuxeo.ecm.webengine.WebException; 055import org.nuxeo.ecm.webengine.jaxrs.context.RequestContext; 056import org.nuxeo.ecm.webengine.jaxrs.session.SessionFactory; 057import org.nuxeo.ecm.webengine.model.Template; 058import org.nuxeo.ecm.webengine.model.WebObject; 059import org.nuxeo.ecm.webengine.model.impl.AbstractResource; 060import org.nuxeo.ecm.webengine.model.impl.ResourceTypeImpl; 061import org.nuxeo.runtime.api.Framework; 062 063/** 064 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 065 */ 066@WebObject(type = "doc") 067@Produces("text/html;charset=UTF-8") 068public class DocResource extends AbstractResource<ResourceTypeImpl> { 069 070 private final Log log = LogFactory.getLog(DocResource.class); 071 072 protected AutomationService service; 073 074 protected List<OperationDocumentation> ops; 075 076 @Override 077 public void initialize(Object... args) { 078 try { 079 service = Framework.getLocalService(AutomationService.class); 080 ops = service.getDocumentation(); 081 } catch (OperationException e) { 082 log.error("Failed to get automation service", e); 083 throw WebException.wrap(e); 084 } 085 } 086 087 protected Template getTemplateFor(String browse) { 088 return getTemplateView("index").arg("browse", browse); 089 } 090 091 protected Template getTemplateView(String name) { 092 Map<String, List<OperationDocumentation>> cats = new HashMap<String, List<OperationDocumentation>>(); 093 for (OperationDocumentation op : ops) { 094 List<OperationDocumentation> list = cats.get(op.getCategory()); 095 if (list == null) { 096 list = new ArrayList<OperationDocumentation>(); 097 cats.put(op.getCategory(), list); 098 } 099 list.add(op); 100 } 101 // sort categories 102 List<String> catNames = new ArrayList<>(); 103 catNames.addAll(cats.keySet()); 104 Collections.sort(catNames); 105 Map<String, List<OperationDocumentation>> scats = new LinkedHashMap<String, List<OperationDocumentation>>(); 106 for (String catName : catNames) { 107 scats.put(catName, cats.get(catName)); 108 } 109 return getView(name).arg("categories", scats).arg("operations", ops); 110 } 111 112 @GET 113 public Object doGet(@QueryParam("id") String id, @QueryParam("browse") String browse) { 114 if (id == null) { 115 return getTemplateFor(browse); 116 } else { 117 OperationDocumentation opDoc = null; 118 for (OperationDocumentation op : ops) { 119 if (op.getId().equals(id)) { 120 opDoc = op; 121 break; 122 } 123 } 124 if (opDoc == null) { 125 throw new WebApplicationException(Response.status(404).build()); 126 } 127 Template tpl = getTemplateFor(browse); 128 tpl.arg("operation", opDoc); 129 CoreSession session = SessionFactory.getSession(); 130 if (((NuxeoPrincipal) session.getPrincipal()).isAdministrator()) { 131 // add yaml format - chains are exposing their operations 132 // so this information should be restricted to administrators. 133 try { 134 ByteArrayOutputStream out = new ByteArrayOutputStream(); 135 YamlWriter.toYaml(out, opDoc); 136 tpl.arg("yaml", out.toString()); 137 } catch (IOException e) { 138 throw WebException.wrap(e); 139 } 140 } 141 return tpl; 142 } 143 } 144 145 protected boolean canManageTraces() { 146 return ((NuxeoPrincipal) WebEngine.getActiveContext().getPrincipal()).isAdministrator(); 147 } 148 149 @GET 150 @Path("/wiki") 151 public Object doGetWiki() { 152 return getTemplateView("wiki"); 153 } 154 155 public boolean isTraceEnabled() { 156 TracerFactory tracerFactory = Framework.getLocalService(TracerFactory.class); 157 return tracerFactory.getRecordingState(); 158 } 159 160 @GET 161 @Path("/toggleTraces") 162 public Object toggleTraces() { 163 if (!canManageTraces()) { 164 return "You can not manage traces"; 165 } 166 TracerFactory tracerFactory = Framework.getLocalService(TracerFactory.class); 167 tracerFactory.toggleRecording(); 168 HttpServletRequest request = RequestContext.getActiveContext().getRequest(); 169 String url = request.getHeader("Referer"); 170 try { 171 return Response.seeOther(new URI(url)).build(); 172 } catch (URISyntaxException e) { 173 throw new RuntimeException(e); 174 } 175 } 176 177 @GET 178 @Path("/toggleStackDisplay") 179 @Produces("text/plain") 180 public Object toggleStackDisplay() { 181 if (!canManageTraces()) { 182 return "You can not manage json exception stack display"; 183 } 184 JsonFactoryManager jsonFactoryManager = Framework.getLocalService(JsonFactoryManager.class); 185 return String.valueOf(jsonFactoryManager.toggleStackDisplay()); 186 } 187 188 @GET 189 @Path("/traces") 190 @Produces("text/plain") 191 public String doGetTrace(@QueryParam("opId") String opId) { 192 if (!canManageTraces()) { 193 return "You can not manage traces"; 194 } 195 TracerFactory tracerFactory = Framework.getLocalService(TracerFactory.class); 196 Trace trace = tracerFactory.getTrace(opId); 197 if (trace != null) { 198 return tracerFactory.getTrace(opId).getFormattedText(); 199 } else { 200 return "no trace"; 201 } 202 } 203 204 public String[] getInputs(OperationDocumentation op) { 205 if (op == null) { 206 throw new IllegalArgumentException("Operation must not be null"); 207 } 208 if (op.signature == null || op.signature.length == 0) { 209 return new String[0]; 210 } 211 String[] result = new String[op.signature.length / 2]; 212 for (int i = 0, k = 0; i < op.signature.length; i += 2, k++) { 213 result[k] = op.signature[i]; 214 } 215 return result; 216 } 217 218 public String[] getOutputs(OperationDocumentation op) { 219 if (op == null) { 220 throw new IllegalArgumentException("Operation must not be null"); 221 } 222 if (op.signature == null || op.signature.length == 0) { 223 return new String[0]; 224 } 225 String[] result = new String[op.signature.length / 2]; 226 for (int i = 1, k = 0; i < op.signature.length; i += 2, k++) { 227 result[k] = op.signature[i]; 228 } 229 return result; 230 } 231 232 public String getInputsAsString(OperationDocumentation op) { 233 String[] result = getInputs(op); 234 if (result == null || result.length == 0) { 235 return "void"; 236 } 237 return StringUtils.join(result, ", "); 238 } 239 240 public String getOutputsAsString(OperationDocumentation op) { 241 String[] result = getOutputs(op); 242 if (result == null || result.length == 0) { 243 return "void"; 244 } 245 return StringUtils.join(result, ", "); 246 } 247 248 public String getParamDefaultValue(OperationDocumentation.Param param) { 249 if (param.values != null && param.values.length > 0) { 250 return StringUtils.join(param.values, ", "); 251 } 252 return ""; 253 } 254 255 public boolean hasOperation(OperationDocumentation op) { 256 if (op == null) { 257 throw new IllegalArgumentException("Operation must not be null"); 258 } 259 if (op.getOperations() == null || op.getOperations().length == 0) { 260 return false; 261 } 262 return true; 263 } 264}