提交 b25d3356 编写于 作者: F Frederik Heremans 提交者: Henry Yan

Added job exception starcktrace in REST

上级 46af8344
......@@ -57,6 +57,7 @@ public final class RestUrls {
public static final String SEGMENT_COLUMNS = "columns";
public static final String SEGMENT_DATA = "data";
public static final String SEGMENT_JOBS = "jobs";
public static final String SEGMENT_JOB_EXCEPTION_STACKTRACE = "exception-stacktrace";
/**
* URL template for the deployment collection: <i>repository/deployments</i>
......@@ -327,6 +328,11 @@ public final class RestUrls {
*/
public static final String[] URL_JOB = {SEGMENT_MANAGEMENT_RESOURCES, SEGMENT_JOBS, "{0}"};
/**
* URL template for the stacktrace of a single job: <i>management/jobs/{0:jobId}/exception-stacktrace</i>
*/
public static final String[] URL_JOB_EXCEPTION_STRACKTRACE = {SEGMENT_MANAGEMENT_RESOURCES, SEGMENT_JOBS, "{0}", SEGMENT_JOB_EXCEPTION_STACKTRACE};
/**
* URL template for the collection of jobs: <i>management/jobs</i>
*/
......
/* 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.
*/
package org.activiti.rest.api.management;
import java.io.ByteArrayInputStream;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.ActivitiObjectNotFoundException;
import org.activiti.engine.runtime.Job;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.SecuredResource;
import org.restlet.data.MediaType;
import org.restlet.representation.InputRepresentation;
import org.restlet.resource.Get;
/**
* @author Frederik Heremans
*/
public class JobExceptionStacktraceResource extends SecuredResource {
@Get
public InputRepresentation getJobStacktrace() {
if (authenticate() == false)
return null;
Job job = getJobFromResponse();
String stackTrace = ActivitiUtil.getManagementService().getJobExceptionStacktrace(job.getId());
if(stackTrace == null) {
throw new ActivitiObjectNotFoundException("Job with id '" + job.getId() + "' doesn't have an exception stacktrace.", String.class);
}
return new InputRepresentation(new ByteArrayInputStream(stackTrace.getBytes()), MediaType.TEXT_PLAIN);
}
protected Job getJobFromResponse() {
String jobId = getAttribute("jobId");
if (jobId == null) {
throw new ActivitiIllegalArgumentException("The jobId cannot be null");
}
Job job = ActivitiUtil.getManagementService().createJobQuery().jobId(jobId).singleResult();
if (job == null) {
throw new ActivitiObjectNotFoundException("Could not find a job with id '" + jobId + "'.", Job.class);
}
return job;
}
}
......@@ -52,6 +52,7 @@ import org.activiti.rest.api.legacy.process.LegacyProcessInstanceResource;
import org.activiti.rest.api.legacy.process.LegacyProcessInstancesResource;
import org.activiti.rest.api.legacy.process.ProcessDefinitionsResource;
import org.activiti.rest.api.legacy.task.LegacyTaskResource;
import org.activiti.rest.api.management.JobExceptionStacktraceResource;
import org.activiti.rest.api.management.JobResource;
import org.activiti.rest.api.management.TableCollectionResource;
import org.activiti.rest.api.management.TableColumnsResource;
......@@ -160,6 +161,7 @@ public class RestServicesInit {
router.attach("/management/tables/{tableName}/columns", TableColumnsResource.class);
router.attach("/management/tables/{tableName}/data", TableDataResource.class);
router.attach("/management/jobs/{jobId}", JobResource.class);
router.attach("/management/jobs/{jobId}/exception-stacktrace", JobExceptionStacktraceResource.class);
router.attach("/query/tasks", TaskQueryResource.class);
router.attach("/query/process-instances", ProcessInstanceQueryResource.class);
......
......@@ -51,8 +51,10 @@ import org.joda.time.format.ISODateTimeFormat;
import org.junit.Assert;
import org.restlet.Component;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.Form;
import org.restlet.data.Protocol;
import org.restlet.data.Status;
import org.restlet.engine.http.header.HeaderConstants;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.slf4j.Logger;
......@@ -429,4 +431,9 @@ public class BaseRestTestCase extends PvmTestCase {
protected String getISODateString(Date time) {
return ISO8601Utils.format(time, true);
}
protected String getMediaType(ClientResource client) {
Form headers = (Form) client.getResponseAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS);
return headers.getFirstValue(HeaderConstants.HEADER_CONTENT_TYPE);
}
}
package org.activiti.rest.api.management;
import java.util.Calendar;
import java.util.Collections;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.impl.util.ClockUtil;
import org.activiti.engine.runtime.Job;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.test.Deployment;
import org.activiti.rest.BaseRestTestCase;
import org.activiti.rest.api.RestUrls;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.ResourceException;
/**
* Test for all REST-operations related to the Job collection and a single
* job resource.
*
* @author Frederik Heremans
*/
public class JobExceptionStacktraceResourceTest extends BaseRestTestCase {
/**
* Test getting the stacktrace for a failed job
*/
@Deployment(resources = {"org/activiti/rest/api/management/JobExceptionStacktraceResourceTest.testTimerProcess.bpmn20.xml"})
public void testGetJobStacktrace() throws Exception {
// Start process, forcing error on job-execution
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("timerProcess",
Collections.singletonMap("error", (Object) Boolean.TRUE));
Job timerJob = managementService.createJobQuery().processInstanceId(processInstance.getId()).singleResult();
assertNotNull(timerJob);
// Force execution of job
try {
managementService.executeJob(timerJob.getId());
fail();
} catch(ActivitiException expected) {
// Ignore, we expect the exception
}
Calendar now = Calendar.getInstance();
now.set(Calendar.MILLISECOND, 0);
ClockUtil.setCurrentTime(now.getTime());
ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_JOB_EXCEPTION_STRACKTRACE, timerJob.getId()));
Representation response = client.get();
assertEquals(Status.SUCCESS_OK, client.getResponse().getStatus());
String stack = response.getText();
assertNotNull(stack);
assertEquals(managementService.getJobExceptionStacktrace(timerJob.getId()), stack);
// Also check content-type
assertEquals(MediaType.TEXT_PLAIN.getName(), getMediaType(client));
}
/**
* Test getting the stacktrace for an unexisting job.
*/
public void testGetStrackForUnexistingJob() throws Exception {
ClientResource client = getAuthenticatedClient(
RestUrls.createRelativeResourceUrl(RestUrls.URL_JOB_EXCEPTION_STRACKTRACE, "unexistingjob"));
try {
client.get();
fail("Exception expected");
} catch(ResourceException expected) {
assertEquals(Status.CLIENT_ERROR_NOT_FOUND, expected.getStatus());
assertEquals("Could not find a job with id 'unexistingjob'.", expected.getStatus().getDescription());
}
}
/**
* Test getting the stacktrace for an unexisting job.
*/
@Deployment(resources = {"org/activiti/rest/api/management/JobExceptionStacktraceResourceTest.testTimerProcess.bpmn20.xml"})
public void testGetStrackForJobWithoutException() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("timerProcess",
Collections.singletonMap("error", (Object) Boolean.FALSE));
Job timerJob = managementService.createJobQuery().processInstanceId(processInstance.getId()).singleResult();
assertNotNull(timerJob);
ClientResource client = getAuthenticatedClient(
RestUrls.createRelativeResourceUrl(RestUrls.URL_JOB_EXCEPTION_STRACKTRACE, timerJob.getId()));
try {
client.get();
fail("Exception expected");
} catch(ResourceException expected) {
assertEquals(Status.CLIENT_ERROR_NOT_FOUND, expected.getStatus());
assertEquals("Job with id '" + timerJob.getId() + "' doesn't have an exception stacktrace.", expected.getStatus().getDescription());
}
}
}
\ No newline at end of file
......@@ -29,10 +29,8 @@ import org.activiti.rest.HttpMultipartRepresentation;
import org.activiti.rest.api.RestUrls;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ObjectNode;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.engine.http.header.HeaderConstants;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.ResourceException;
......@@ -442,9 +440,4 @@ public class TaskVariableResourceTest extends BaseRestTestCase {
}
}
}
protected String getMediaType(ClientResource client) {
Form headers = (Form) client.getResponseAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS);
return headers.getFirstValue(HeaderConstants.HEADER_CONTENT_TYPE);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="timerProcess">
<startEvent id="start" />
<sequenceFlow id="flow1" sourceRef="start" targetRef="task" />
<userTask id="task" />
<boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="task">
<timerEventDefinition>
<timeDuration>PT4H</timeDuration>
</timerEventDefinition>
</boundaryEvent>
<sequenceFlow id="flow3" sourceRef="escalationTimer" targetRef="exclusiveGw" />
<exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" />
<sequenceFlow sourceRef="exclusiveGw" targetRef="error">
<conditionExpression xsi:type="tFormalExpression">${error == true}</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="exclusiveGw" targetRef="afterTimerTest" />
<scriptTask id="error" scriptFormat="unexistinglanguage">
<script>
// Using unexisting language to force error
</script>
</scriptTask>
<userTask id="afterTimerTest" />
<sequenceFlow id="flow4" sourceRef="afterTimerTest" targetRef="end" />
<sequenceFlow id="flow2" sourceRef="task" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>
\ No newline at end of file
......@@ -4132,7 +4132,7 @@ Only the attachment name is required to create a new attachment.
<section>
<title>Execute a single job</title>
<para>
<programlisting>PUT managemebt/jobs/{jobId}</programlisting>
<programlisting>PUT management/jobs/{jobId}</programlisting>
</para>
<para>
<emphasis role="bold">Body JSON:</emphasis>
......@@ -4188,6 +4188,57 @@ Only the attachment name is required to create a new attachment.
</table>
</para>
</section>
<section>
<title>Get the exception stacktrace for a job</title>
<para>
<programlisting>GET management/jobs/{jobId}/exception-stracktrace</programlisting>
</para>
<para>
<table>
<title>URL parameters</title>
<tgroup cols='2'>
<thead>
<row>
<entry>Parameter</entry>
<entry>Description</entry>
<entry>Required</entry>
</row>
</thead>
<tbody>
<row>
<entry>jobId</entry>
<entry>Id of the job to get the stacktrace for.</entry>
<entry>Yes</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
<table>
<title>Response codes</title>
<tgroup cols='2'>
<thead>
<row>
<entry>Response code</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>200</entry>
<entry>Indicates the requested job was not found and the stacktrace has been returned. The response contains the raw stacktrace and always has a Content-type of <literal>text/plain</literal>.</entry>
</row>
<row>
<entry>404</entry>
<entry>Indicates the requested job was not found or the job doesn't have an exception stacktrace. Status-description contains additional information about the error.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
</section>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册