Search in sources :

Example 31 with TasksClient

use of org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient in project alfresco-remote-api by Alfresco.

the class TaskWorkflowApiTest method testUpdateTaskAuthorization.

@Test
@SuppressWarnings("unchecked")
public void testUpdateTaskAuthorization() throws Exception {
    RequestContext requestContext = initApiClientWithTestUser();
    String initiator = getOtherPersonInNetwork(requestContext.getRunAsUser(), requestContext.getNetworkId()).getId();
    ProcessInstance processInstance = startAdhocProcess(initiator, requestContext.getNetworkId(), null);
    try {
        Task task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        TasksClient tasksClient = publicApiClient.tasksClient();
        // Updating the task when NOT assignee/owner or initiator results in an error
        JSONObject taskBody = new JSONObject();
        taskBody.put("name", "Updated name");
        List<String> selectedFields = new ArrayList<String>();
        selectedFields.addAll(Arrays.asList(new String[] { "name" }));
        try {
            tasksClient.updateTask(task.getId(), taskBody, selectedFields);
            fail("Exception expected");
        } catch (PublicApiException expected) {
            assertEquals(HttpStatus.FORBIDDEN.value(), expected.getHttpResponse().getStatusCode());
            assertErrorSummary("Permission was denied", expected.getHttpResponse());
        }
        // Set assignee to current user, update should succeed
        activitiProcessEngine.getTaskService().setAssignee(task.getId(), requestContext.getRunAsUser());
        taskBody.put("name", "Updated name by assignee");
        JSONObject result = tasksClient.updateTask(task.getId(), taskBody, selectedFields);
        assertEquals("Updated name by assignee", result.get("name"));
        task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        assertNotNull(task);
        assertEquals("Updated name by assignee", task.getName());
        // Set owner to current user, update should succeed
        activitiProcessEngine.getTaskService().setAssignee(task.getId(), null);
        activitiProcessEngine.getTaskService().setOwner(task.getId(), requestContext.getRunAsUser());
        taskBody.put("name", "Updated name by owner");
        result = tasksClient.updateTask(task.getId(), taskBody, selectedFields);
        assertEquals("Updated name by owner", result.get("name"));
        task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        assertNotNull(task);
        assertEquals("Updated name by owner", task.getName());
        // Update as process initiator
        taskBody.put("name", "Updated name by initiator");
        requestContext.setRunAsUser(initiator);
        result = tasksClient.updateTask(task.getId(), taskBody, selectedFields);
        assertEquals("Updated name by initiator", result.get("name"));
        task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        assertNotNull(task);
        assertEquals("Updated name by initiator", task.getName());
        // Update as administrator
        String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + requestContext.getNetworkId();
        publicApiClient.setRequestContext(new RequestContext(TenantUtil.DEFAULT_TENANT, tenantAdmin));
        taskBody.put("name", "Updated name by admin");
        result = tasksClient.updateTask(task.getId(), taskBody, selectedFields);
        assertEquals("Updated name by admin", result.get("name"));
        task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        assertNotNull(task);
        assertEquals("Updated name by admin", task.getName());
    } finally {
        cleanupProcessInstance(processInstance);
    }
}
Also used : PublicApiException(org.alfresco.rest.api.tests.client.PublicApiException) Task(org.activiti.engine.task.Task) JSONObject(org.json.simple.JSONObject) TasksClient(org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient) ArrayList(java.util.ArrayList) ProcessInstance(org.activiti.engine.runtime.ProcessInstance) RequestContext(org.alfresco.rest.api.tests.client.RequestContext) Test(org.junit.Test)

Example 32 with TasksClient

use of org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient in project alfresco-remote-api by Alfresco.

the class TaskWorkflowApiTest method testUpdateTaskVariablesPresentInModel.

@Test
@SuppressWarnings("unchecked")
public void testUpdateTaskVariablesPresentInModel() throws Exception {
    RequestContext requestContext = initApiClientWithTestUser();
    ProcessInstance processInstance = startAdhocProcess(requestContext.getRunAsUser(), requestContext.getNetworkId(), null);
    try {
        Task task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        assertNotNull(task);
        Map<String, Object> actualLocalVariables = activitiProcessEngine.getTaskService().getVariablesLocal(task.getId());
        Map<String, Object> actualGlobalVariables = activitiProcessEngine.getRuntimeService().getVariables(processInstance.getId());
        assertEquals(5, actualGlobalVariables.size());
        assertEquals(8, actualLocalVariables.size());
        // Update a global value that is present in the model with type given
        JSONObject variableBody = new JSONObject();
        variableBody.put("name", "bpm_percentComplete");
        variableBody.put("value", 20);
        variableBody.put("type", "d:int");
        variableBody.put("scope", "global");
        TasksClient tasksClient = publicApiClient.tasksClient();
        JSONObject resultEntry = tasksClient.updateTaskVariable(task.getId(), "bpm_percentComplete", variableBody);
        assertNotNull(resultEntry);
        JSONObject result = (JSONObject) resultEntry.get("entry");
        assertEquals("bpm_percentComplete", result.get("name"));
        assertEquals(20L, result.get("value"));
        assertEquals("d:int", result.get("type"));
        assertEquals("global", result.get("scope"));
        assertEquals(20, activitiProcessEngine.getRuntimeService().getVariable(processInstance.getId(), "bpm_percentComplete"));
        // Update a local value that is present in the model with name and scope, omitting type to see if type is deducted from model
        variableBody = new JSONObject();
        variableBody.put("name", "bpm_percentComplete");
        variableBody.put("value", 30);
        variableBody.put("scope", "local");
        result = resultEntry = tasksClient.updateTaskVariable(task.getId(), "bpm_percentComplete", variableBody);
        assertNotNull(resultEntry);
        result = (JSONObject) resultEntry.get("entry");
        assertEquals("bpm_percentComplete", result.get("name"));
        assertEquals(30L, result.get("value"));
        assertEquals("d:int", result.get("type"));
        assertEquals("local", result.get("scope"));
        assertEquals(30, activitiProcessEngine.getTaskService().getVariable(task.getId(), "bpm_percentComplete"));
        // Global variable should remain unaffected
        assertEquals(20, activitiProcessEngine.getRuntimeService().getVariable(processInstance.getId(), "bpm_percentComplete"));
    } finally {
        cleanupProcessInstance(processInstance);
    }
}
Also used : Task(org.activiti.engine.task.Task) JSONObject(org.json.simple.JSONObject) TasksClient(org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient) ProcessInstance(org.activiti.engine.runtime.ProcessInstance) JSONObject(org.json.simple.JSONObject) RequestContext(org.alfresco.rest.api.tests.client.RequestContext) Test(org.junit.Test)

Example 33 with TasksClient

use of org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient in project alfresco-remote-api by Alfresco.

the class TaskWorkflowApiTest method testCreateTaskVariablesPresentInModel.

@Test
@SuppressWarnings("unchecked")
public void testCreateTaskVariablesPresentInModel() throws Exception {
    RequestContext requestContext = initApiClientWithTestUser();
    ProcessInstance processInstance = startAdhocProcess(requestContext.getRunAsUser(), requestContext.getNetworkId(), null);
    try {
        Task task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        assertNotNull(task);
        Map<String, Object> actualLocalVariables = activitiProcessEngine.getTaskService().getVariablesLocal(task.getId());
        Map<String, Object> actualGlobalVariables = activitiProcessEngine.getRuntimeService().getVariables(processInstance.getId());
        assertEquals(5, actualGlobalVariables.size());
        assertEquals(8, actualLocalVariables.size());
        // Update a global value that is present in the model with type given
        JSONArray variablesArray = new JSONArray();
        JSONObject variableBody = new JSONObject();
        variableBody.put("name", "bpm_percentComplete");
        variableBody.put("value", 20);
        variableBody.put("type", "d:int");
        variableBody.put("scope", "global");
        variablesArray.add(variableBody);
        variableBody = new JSONObject();
        variableBody.put("name", "bpm_workflowPriority");
        variableBody.put("value", 50);
        variableBody.put("type", "d:int");
        variableBody.put("scope", "local");
        variablesArray.add(variableBody);
        TasksClient tasksClient = publicApiClient.tasksClient();
        JSONObject result = tasksClient.createTaskVariables(task.getId(), variablesArray);
        assertNotNull(result);
        JSONObject resultObject = (JSONObject) result.get("list");
        JSONArray resultList = (JSONArray) resultObject.get("entries");
        assertEquals(2, resultList.size());
        JSONObject firstResultObject = (JSONObject) ((JSONObject) resultList.get(0)).get("entry");
        assertEquals("bpm_percentComplete", firstResultObject.get("name"));
        assertEquals(20L, firstResultObject.get("value"));
        assertEquals("d:int", firstResultObject.get("type"));
        assertEquals("global", firstResultObject.get("scope"));
        assertEquals(20, activitiProcessEngine.getRuntimeService().getVariable(processInstance.getId(), "bpm_percentComplete"));
        JSONObject secondResultObject = (JSONObject) ((JSONObject) resultList.get(1)).get("entry");
        assertEquals("bpm_workflowPriority", secondResultObject.get("name"));
        assertEquals(50L, secondResultObject.get("value"));
        assertEquals("d:int", secondResultObject.get("type"));
        assertEquals("local", secondResultObject.get("scope"));
        assertEquals(50, activitiProcessEngine.getTaskService().getVariable(task.getId(), "bpm_workflowPriority"));
    } finally {
        cleanupProcessInstance(processInstance);
    }
}
Also used : Task(org.activiti.engine.task.Task) JSONObject(org.json.simple.JSONObject) TasksClient(org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient) JSONArray(org.json.simple.JSONArray) ProcessInstance(org.activiti.engine.runtime.ProcessInstance) JSONObject(org.json.simple.JSONObject) RequestContext(org.alfresco.rest.api.tests.client.RequestContext) Test(org.junit.Test)

Example 34 with TasksClient

use of org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient in project alfresco-remote-api by Alfresco.

the class TaskWorkflowApiTest method testGetTasks.

@Test
@SuppressWarnings("unchecked")
public void testGetTasks() throws Exception {
    // Alter current engine date
    Calendar createdCal = Calendar.getInstance();
    createdCal.set(Calendar.MILLISECOND, 0);
    Clock actiClock = activitiProcessEngine.getProcessEngineConfiguration().getClock();
    actiClock.setCurrentTime(createdCal.getTime());
    RequestContext requestContext = initApiClientWithTestUser();
    String otherPerson = getOtherPersonInNetwork(requestContext.getRunAsUser(), requestContext.getNetworkId()).getId();
    RequestContext otherContext = new RequestContext(requestContext.getNetworkId(), otherPerson);
    ProcessInstance processInstance = startAdhocProcess(requestContext.getRunAsUser(), requestContext.getNetworkId(), null);
    try {
        Task task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        assertNotNull(task);
        // Set some task-properties not set by process-definition
        Calendar dueDateCal = Calendar.getInstance();
        dueDateCal.set(Calendar.MILLISECOND, 0);
        dueDateCal.add(Calendar.DAY_OF_YEAR, 1);
        Date dueDate = dueDateCal.getTime();
        task.setDescription("This is a test description");
        task.setAssignee(requestContext.getRunAsUser());
        task.setOwner("john");
        task.setDueDate(dueDate);
        activitiProcessEngine.getTaskService().saveTask(task);
        TasksClient tasksClient = publicApiClient.tasksClient();
        // Check resulting task
        JSONObject taskListJSONObject = tasksClient.findTasks(null);
        assertNotNull(taskListJSONObject);
        JSONArray jsonEntries = (JSONArray) taskListJSONObject.get("entries");
        assertEquals(1, jsonEntries.size());
        JSONObject taskJSONObject = (JSONObject) ((JSONObject) jsonEntries.get(0)).get("entry");
        assertNotNull(taskJSONObject);
        assertEquals(task.getId(), taskJSONObject.get("id"));
        assertEquals(processInstance.getId(), taskJSONObject.get("processId"));
        assertEquals(processInstance.getProcessDefinitionId(), taskJSONObject.get("processDefinitionId"));
        assertEquals("adhocTask", taskJSONObject.get("activityDefinitionId"));
        assertEquals("Adhoc Task", taskJSONObject.get("name"));
        assertEquals("This is a test description", taskJSONObject.get("description"));
        assertEquals(requestContext.getRunAsUser(), taskJSONObject.get("assignee"));
        assertEquals("john", taskJSONObject.get("owner"));
        assertEquals(dueDate, parseDate(taskJSONObject, "dueAt"));
        // experiment
        assertEquals(createdCal.getTime().toString(), parseDate(taskJSONObject, "startedAt").toString());
        assertEquals(createdCal.getTime(), parseDate(taskJSONObject, "startedAt"));
        assertEquals(2l, taskJSONObject.get("priority"));
        assertEquals("wf:adhocTask", taskJSONObject.get("formResourceKey"));
        assertNull(taskJSONObject.get("endedAt"));
        assertNull(taskJSONObject.get("durationInMs"));
        // get tasks with user that has no assigned tasks
        publicApiClient.setRequestContext(otherContext);
        taskListJSONObject = tasksClient.findTasks(null);
        assertNotNull(taskListJSONObject);
        jsonEntries = (JSONArray) taskListJSONObject.get("entries");
        assertEquals(0, jsonEntries.size());
        // get tasks for user which has one task assigned by somebody else
        publicApiClient.setRequestContext(requestContext);
        JSONObject taskBody = new JSONObject();
        taskBody.put("assignee", otherContext.getRunAsUser());
        List<String> selectedFields = new ArrayList<String>();
        selectedFields.addAll(Arrays.asList(new String[] { "assignee" }));
        tasksClient.updateTask(task.getId(), taskBody, selectedFields);
        publicApiClient.setRequestContext(otherContext);
        taskListJSONObject = tasksClient.findTasks(null);
        assertNotNull(taskListJSONObject);
        jsonEntries = (JSONArray) taskListJSONObject.get("entries");
        assertEquals(1, jsonEntries.size());
    } finally {
        cleanupProcessInstance(processInstance);
    }
}
Also used : Task(org.activiti.engine.task.Task) JSONObject(org.json.simple.JSONObject) Calendar(java.util.Calendar) TasksClient(org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient) JSONArray(org.json.simple.JSONArray) ArrayList(java.util.ArrayList) ProcessInstance(org.activiti.engine.runtime.ProcessInstance) RequestContext(org.alfresco.rest.api.tests.client.RequestContext) Clock(org.activiti.engine.runtime.Clock) Date(java.util.Date) Test(org.junit.Test)

Example 35 with TasksClient

use of org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient in project alfresco-remote-api by Alfresco.

the class TaskWorkflowApiTest method testGetTaskItems.

@Test
public void testGetTaskItems() throws Exception {
    final RequestContext requestContext = initApiClientWithTestUser();
    String otherPerson = getOtherPersonInNetwork(requestContext.getRunAsUser(), requestContext.getNetworkId()).getId();
    RequestContext otherContext = new RequestContext(requestContext.getNetworkId(), otherPerson);
    String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + requestContext.getNetworkId();
    RequestContext adminContext = new RequestContext(requestContext.getNetworkId(), tenantAdmin);
    // Create test-document and add to package
    NodeRef[] docNodeRefs = createTestDocuments(requestContext);
    ProcessInfo processInfo = startAdhocProcess(requestContext, docNodeRefs);
    final Task task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInfo.getId()).singleResult();
    assertNotNull(task);
    activitiProcessEngine.getTaskService().setAssignee(task.getId(), null);
    try {
        TasksClient tasksClient = publicApiClient.tasksClient();
        JSONObject itemsJSON = tasksClient.findTaskItems(task.getId());
        assertNotNull(itemsJSON);
        JSONArray entriesJSON = (JSONArray) itemsJSON.get("entries");
        assertNotNull(entriesJSON);
        assertTrue(entriesJSON.size() == 2);
        boolean doc1Found = false;
        boolean doc2Found = false;
        for (Object entryObject : entriesJSON) {
            JSONObject entryObjectJSON = (JSONObject) entryObject;
            JSONObject entryJSON = (JSONObject) entryObjectJSON.get("entry");
            if (entryJSON.get("name").equals("Test Doc1")) {
                doc1Found = true;
                assertEquals(docNodeRefs[0].getId(), entryJSON.get("id"));
                assertEquals("Test Doc1", entryJSON.get("name"));
                assertEquals("Test Doc1 Title", entryJSON.get("title"));
                assertEquals("Test Doc1 Description", entryJSON.get("description"));
                assertNotNull(entryJSON.get("createdAt"));
                assertEquals(requestContext.getRunAsUser(), entryJSON.get("createdBy"));
                assertNotNull(entryJSON.get("modifiedAt"));
                assertEquals(requestContext.getRunAsUser(), entryJSON.get("modifiedBy"));
                assertNotNull(entryJSON.get("size"));
                assertNotNull(entryJSON.get("mimeType"));
            } else {
                doc2Found = true;
                assertEquals(docNodeRefs[1].getId(), entryJSON.get("id"));
                assertEquals("Test Doc2", entryJSON.get("name"));
                assertEquals("Test Doc2 Title", entryJSON.get("title"));
                assertEquals("Test Doc2 Description", entryJSON.get("description"));
                assertNotNull(entryJSON.get("createdAt"));
                assertEquals(requestContext.getRunAsUser(), entryJSON.get("createdBy"));
                assertNotNull(entryJSON.get("modifiedAt"));
                assertEquals(requestContext.getRunAsUser(), entryJSON.get("modifiedBy"));
                assertNotNull(entryJSON.get("size"));
                assertNotNull(entryJSON.get("mimeType"));
            }
        }
        assertTrue(doc1Found);
        assertTrue(doc2Found);
        // get with admin
        publicApiClient.setRequestContext(adminContext);
        itemsJSON = tasksClient.findTaskItems(task.getId());
        assertNotNull(itemsJSON);
        entriesJSON = (JSONArray) itemsJSON.get("entries");
        assertNotNull(entriesJSON);
        assertTrue(entriesJSON.size() == 2);
        // get with non involved user
        publicApiClient.setRequestContext(otherContext);
        try {
            tasksClient.findTaskItems(task.getId());
            fail("Expected exception");
        } catch (PublicApiException e) {
            assertEquals(403, e.getHttpResponse().getStatusCode());
        }
        // get with candidate user
        activitiProcessEngine.getTaskService().addCandidateUser(task.getId(), otherContext.getRunAsUser());
        publicApiClient.setRequestContext(otherContext);
        itemsJSON = tasksClient.findTaskItems(task.getId());
        assertNotNull(itemsJSON);
        entriesJSON = (JSONArray) itemsJSON.get("entries");
        assertNotNull(entriesJSON);
        assertTrue(entriesJSON.size() == 2);
        // get with user from candidate group with no assignee
        List<MemberOfSite> memberships = getTestFixture().getNetwork(otherContext.getNetworkId()).getSiteMemberships(otherContext.getRunAsUser());
        assertTrue(memberships.size() > 0);
        MemberOfSite memberOfSite = memberships.get(0);
        String group = "GROUP_site_" + memberOfSite.getSiteId() + "_" + memberOfSite.getRole().name();
        activitiProcessEngine.getTaskService().deleteCandidateUser(task.getId(), otherContext.getRunAsUser());
        activitiProcessEngine.getTaskService().addCandidateGroup(task.getId(), group);
        publicApiClient.setRequestContext(otherContext);
        itemsJSON = tasksClient.findTaskItems(task.getId());
        assertNotNull(itemsJSON);
        entriesJSON = (JSONArray) itemsJSON.get("entries");
        assertNotNull(entriesJSON);
        assertTrue(entriesJSON.size() == 2);
        // get with user from candidate group with assignee
        activitiProcessEngine.getTaskService().setAssignee(task.getId(), requestContext.getRunAsUser());
        try {
            tasksClient.findTaskItems(task.getId());
            fail("Expected exception");
        } catch (PublicApiException e) {
            assertEquals(403, e.getHttpResponse().getStatusCode());
        }
        // invalid task id
        publicApiClient.setRequestContext(requestContext);
        try {
            tasksClient.findTaskItems("fakeid");
            fail("Expected exception");
        } catch (PublicApiException e) {
            assertEquals(404, e.getHttpResponse().getStatusCode());
        }
        // get items from completed task with initiator
        TenantUtil.runAsUserTenant(new TenantRunAsWork<Void>() {

            @Override
            public Void doWork() throws Exception {
                activitiProcessEngine.getTaskService().complete(task.getId());
                return null;
            }
        }, requestContext.getRunAsUser(), requestContext.getNetworkId());
        publicApiClient.setRequestContext(requestContext);
        itemsJSON = tasksClient.findTaskItems(task.getId());
        assertNotNull(itemsJSON);
        entriesJSON = (JSONArray) itemsJSON.get("entries");
        assertNotNull(entriesJSON);
        assertTrue(entriesJSON.size() == 2);
        // get items from completed task with user from candidate group
        publicApiClient.setRequestContext(otherContext);
        try {
            tasksClient.findTaskItems(task.getId());
            fail("Expected exception");
        } catch (PublicApiException e) {
            assertEquals(403, e.getHttpResponse().getStatusCode());
        }
    } finally {
        cleanupProcessInstance(processInfo.getId());
    }
}
Also used : Task(org.activiti.engine.task.Task) TasksClient(org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient) JSONArray(org.json.simple.JSONArray) MemberOfSite(org.alfresco.rest.api.tests.client.data.MemberOfSite) ProcessInfo(org.alfresco.rest.workflow.api.model.ProcessInfo) PublicApiException(org.alfresco.rest.api.tests.client.PublicApiException) PublicApiException(org.alfresco.rest.api.tests.client.PublicApiException) NodeRef(org.alfresco.service.cmr.repository.NodeRef) JSONObject(org.json.simple.JSONObject) JSONObject(org.json.simple.JSONObject) RequestContext(org.alfresco.rest.api.tests.client.RequestContext) Test(org.junit.Test)

Aggregations

TasksClient (org.alfresco.rest.workflow.api.tests.WorkflowApiClient.TasksClient)41 Test (org.junit.Test)41 RequestContext (org.alfresco.rest.api.tests.client.RequestContext)39 Task (org.activiti.engine.task.Task)38 JSONObject (org.json.simple.JSONObject)36 ProcessInstance (org.activiti.engine.runtime.ProcessInstance)32 PublicApiException (org.alfresco.rest.api.tests.client.PublicApiException)22 ArrayList (java.util.ArrayList)17 JSONArray (org.json.simple.JSONArray)14 HashMap (java.util.HashMap)9 ProcessInfo (org.alfresco.rest.workflow.api.model.ProcessInfo)8 Calendar (java.util.Calendar)5 Date (java.util.Date)5 TestNetwork (org.alfresco.rest.api.tests.RepoService.TestNetwork)5 MemberOfSite (org.alfresco.rest.api.tests.client.data.MemberOfSite)5 NodeRef (org.alfresco.service.cmr.repository.NodeRef)4 HistoricTaskInstance (org.activiti.engine.history.HistoricTaskInstance)3 HashSet (java.util.HashSet)2 TaskService (org.activiti.engine.TaskService)2 Clock (org.activiti.engine.runtime.Clock)2