/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * 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.jbpm.services.task.jaxb; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import org.jbpm.services.task.MvelFilePath; import org.jbpm.services.task.commands.CancelDeadlineCommand; import org.jbpm.services.task.commands.CompositeCommand; import org.jbpm.services.task.commands.GetTaskAssignedAsPotentialOwnerCommand; import org.jbpm.services.task.commands.ProcessSubTaskCommand; import org.jbpm.services.task.commands.SkipTaskCommand; import org.jbpm.services.task.commands.StartTaskCommand; import org.jbpm.services.task.impl.factories.TaskFactory; import org.jbpm.services.task.impl.model.CommentImpl; import org.jbpm.services.task.impl.model.ContentImpl; import org.jbpm.services.task.impl.model.I18NTextImpl; import org.jbpm.services.task.impl.model.UserImpl; import org.jbpm.services.task.impl.model.xml.JaxbComment; import org.jbpm.services.task.impl.model.xml.JaxbContent; import org.jbpm.services.task.impl.model.xml.JaxbI18NText; import org.jbpm.services.task.impl.model.xml.JaxbTask; import org.jbpm.services.task.utils.ContentMarshallerHelper; import org.junit.Assume; import org.junit.Test; import org.kie.api.task.model.Attachment; import org.kie.api.task.model.Comment; import org.kie.api.task.model.I18NText; import org.kie.api.task.model.Status; import org.kie.api.task.model.Task; import org.kie.api.task.model.TaskData; import org.kie.api.task.model.User; import org.kie.internal.query.QueryFilter; import org.kie.internal.task.api.TaskModelProvider; import org.kie.internal.task.api.model.AccessType; import org.kie.internal.task.api.model.InternalAttachment; import org.kie.internal.task.api.model.InternalComment; import org.kie.internal.task.api.model.InternalI18NText; import org.kie.internal.task.api.model.InternalOrganizationalEntity; import org.kie.internal.task.api.model.InternalTask; import org.kie.internal.task.api.model.InternalTaskData; import org.kie.internal.task.api.model.SubTasksStrategy; import org.kie.test.util.compare.ComparePair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AbstractTaskSerializationTest { protected final Logger logger; public AbstractTaskSerializationTest() { logger = LoggerFactory.getLogger(this.getClass()); } public abstract <T> T testRoundTrip(T input) throws Exception; public abstract TestType getType(); public abstract void addClassesToSerializationContext(Class<?>... extraClass); public enum TestType { JAXB, JSON, YAML; } protected static Random random = new Random(); // TESTS ---------------------------------------------------------------------------------------------------------------------- @Test public void jaxbTaskTest() throws Exception { // Yaml serialization requires major changes in order to be supported.. :/ Assume.assumeTrue(!getType().equals(TestType.YAML)); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("now", new Date()); InputStream stream = getClass().getResourceAsStream(MvelFilePath.FullTask); assertNotNull("Could not load file: " + MvelFilePath.FullTask, stream); Reader reader = new InputStreamReader(stream); InternalTask task = (InternalTask) TaskFactory.evalTask(reader, vars); // fill task task.setFormName("Bruno's Form"); task.setId(23L); task.setSubTaskStrategy(SubTasksStrategy.EndParentOnAllSubTasksEnd); for( I18NText text : task.getNames() ) { ((InternalI18NText) text).setId((long) random.nextInt(1000)); } for( I18NText text : task.getSubjects() ) { ((InternalI18NText) text).setId((long) random.nextInt(1000)); } for( I18NText text : task.getDescriptions() ) { ((InternalI18NText) text).setId((long) random.nextInt(1000)); } // fill task -> task data InternalTaskData taskData = (InternalTaskData) task.getTaskData(); taskData.setOutputAccessType(AccessType.Inline); taskData.setFaultAccessType(AccessType.Unknown); taskData.setTaskInputVariables(new HashMap<String, Object>()); taskData.setTaskOutputVariables(new HashMap<String, Object>()); // fill task -> task data -> comment String payload = "brainwashArmitageRecruitCaseGetPasswordFromLady3JaneAscentToStraylightIcebreakerUniteWithNeuromancer"; InternalComment comment = (InternalComment) TaskModelProvider.getFactory().newComment(); comment.setId(42); comment.setText(payload); comment.setAddedAt(new Date()); User user = TaskModelProvider.getFactory().newUser(); ((InternalOrganizationalEntity) user).setId("Case"); comment.setAddedBy(user); taskData.addComment(comment); // fill task -> task data -> attachment InternalAttachment attach = (InternalAttachment) TaskModelProvider.getFactory().newAttachment(); attach.setId(10); attach.setName("virus"); attach.setContentType("ROM"); attach.setAttachedAt(new Date()); user = TaskModelProvider.getFactory().newUser(); ((InternalOrganizationalEntity) user).setId("Wintermute"); attach.setAttachedBy(user); attach.setSize(payload.getBytes().length); attach.setAttachmentContentId(comment.getId()); taskData.addAttachment(attach); JaxbTask xmlTask = new JaxbTask(task); verifyThatAllFieldsAreFilledOrUnsupported(xmlTask, InternalTask.class); verifyThatAllFieldsAreFilledOrUnsupported(xmlTask.getTaskData(), TaskData.class); verifyThatAllFieldsAreFilledOrUnsupported(xmlTask.getTaskData().getAttachments().get(0), Attachment.class); verifyThatAllFieldsAreFilledOrUnsupported(xmlTask.getTaskData().getComments().get(0), Comment.class); assertNotNull(xmlTask.getNames()); assertTrue(xmlTask.getNames().size() > 0); JaxbTask bornAgainTask = testRoundTrip(xmlTask); assertNotNull(bornAgainTask.getNames()); assertTrue("Round-tripped task has empty 'names' list!", !bornAgainTask.getNames().isEmpty()); ComparePair compare = new ComparePair(task, bornAgainTask, Task.class); compare.recursiveCompare(); assertNotNull(((InternalTask) xmlTask).getFormName()); assertEquals(((InternalTask) xmlTask).getFormName(), ((InternalTask) bornAgainTask).getFormName()); Task realTask = xmlTask.getTask(); verifyThatXmlFieldsAreFilled(realTask, xmlTask, InternalTask.class, "deadlines"); verifyThatXmlFieldsAreFilled(realTask.getTaskData(), xmlTask.getTaskData(), TaskData.class, "taskInputVariables", "taskOutputVariables"); } private void verifyThatXmlFieldsAreFilled(Object realInst, Object xmlInst, Class interfaze, String... ignoreFields ) { Set<String> ignoreFieldSet = new HashSet<String>(Arrays.asList(ignoreFields)); assertNotNull( interfaze.getSimpleName() + " (XML) instance is null", xmlInst); assertNotNull( interfaze.getSimpleName() + " (XML) instance is null", realInst); String methodName = null; try { for( Method getMethod : interfaze.getMethods() ) { methodName = getMethod.getName(); if( ! methodName.startsWith("get") ) { continue; } try { String fieldName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4); if( ignoreFieldSet.contains(fieldName) ) { continue; } // xml Inst Object xmlFieldValue = getMethod.invoke(xmlInst); // real Inst Object fieldValue = getMethod.invoke(realInst); if( Enum.class.isAssignableFrom(xmlFieldValue.getClass()) || xmlFieldValue.getClass().isEnum() ) { assertEquals( interfaze.getSimpleName() + "." + fieldName + " value has not been copied", xmlFieldValue, fieldValue); } else if( xmlFieldValue.getClass().getPackage().getName().contains("org.kie") || xmlFieldValue.getClass().getPackage().getName().contains("org.jbpm") ) { assertNotNull(interfaze.getSimpleName() + "." + fieldName + " value is empty", fieldValue); } else if( xmlFieldValue.getClass().isArray() ) { List xmlList = Arrays.asList(xmlFieldValue); List realList = Arrays.asList(fieldValue); assertEquals( interfaze.getSimpleName() + "." + fieldName + " value has unequal list size", xmlList.size(), realList.size()); for( int i = 0; i < xmlList.size(); ++i ) { Object xmlElem = xmlList.get(i); Object realElem = realList.get(i); verifyThatXmlFieldsAreFilled(realElem, xmlElem, xmlElem.getClass().getInterfaces()[0]); } } else if( List.class.isAssignableFrom(xmlFieldValue.getClass()) ) { List xmlList = (List) xmlFieldValue; List realList = (List) fieldValue; assertEquals( interfaze.getSimpleName() + "." + fieldName + " value has unequal list size", xmlList.size(), realList.size()); for( int i = 0; i < xmlList.size(); ++i ) { Object xmlElem = xmlList.get(i); Object realElem = realList.get(i); verifyThatXmlFieldsAreFilled(realElem, xmlElem, xmlElem.getClass().getInterfaces()[0]); } } else { assertEquals( interfaze.getSimpleName() + "." + fieldName + " value has not been copied", xmlFieldValue, fieldValue); } } catch( InvocationTargetException ite ) { Throwable cause = ite.getCause(); if( cause instanceof UnsupportedOperationException && cause.getMessage().contains("not supported on the JAXB") ) { continue; } throw ite; } } } catch( Exception e ) { logger.error("Test failed: " + e.getMessage(), e); fail( "Unable to verify field via method " + methodName + ": " + e.getMessage()); } } private void verifyThatAllFieldsAreFilledOrUnsupported(Object instance, Class interfaze ) { assertNotNull( interfaze.getSimpleName() + " instance is null", instance); String methodName = null; try { for( Method getMethod : interfaze.getMethods() ) { methodName = getMethod.getName(); if( ! methodName.startsWith("get") ) { continue; } try { Object fieldValue = getMethod.invoke(instance); String fieldName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4); assertNotNull(interfaze.getSimpleName() + "." + fieldName + " is null", fieldValue); if( fieldValue instanceof Number ) { assertFalse(interfaze.getSimpleName() + "." + fieldName + " is -1", ((Number) fieldValue).intValue() < 0 ); } } catch( InvocationTargetException ite ) { Throwable cause = ite.getCause(); if( cause instanceof UnsupportedOperationException && cause.getMessage().contains("not supported on the JAXB") ) { continue; } throw ite; } } } catch( Exception e ) { logger.error("Test failed: " + e.getMessage(), e); fail( "Unable to verify field via method " + methodName + ": " + e.getMessage()); } } @Test public void taskCompositeCommandCanBeSerialized() throws Exception { Assume.assumeTrue(TestType.JAXB.equals(getType())); addClassesToSerializationContext(CompositeCommand.class); addClassesToSerializationContext(StartTaskCommand.class); addClassesToSerializationContext(CancelDeadlineCommand.class); CompositeCommand<Void> cmd = new CompositeCommand<Void>(new StartTaskCommand(1, "john"), new CancelDeadlineCommand(1, true, false)); CompositeCommand<?> returned = testRoundTrip(cmd); assertNotNull(returned); assertNotNull(returned.getMainCommand()); assertTrue(returned.getMainCommand() instanceof StartTaskCommand); assertEquals(Long.valueOf(1), returned.getTaskId()); assertNotNull(returned.getCommands()); assertEquals(1, returned.getCommands().size()); } @Test public void taskCompositeCommandMultipleCanBeSerialized() throws Exception { Assume.assumeTrue(TestType.JAXB.equals(getType())); addClassesToSerializationContext(CompositeCommand.class); addClassesToSerializationContext(SkipTaskCommand.class); addClassesToSerializationContext(ProcessSubTaskCommand.class); addClassesToSerializationContext(CancelDeadlineCommand.class); CompositeCommand<Void> cmd = new CompositeCommand<Void>(new SkipTaskCommand(1, "john"), new ProcessSubTaskCommand(1, "john"), new CancelDeadlineCommand(1, true, true)); CompositeCommand<?> returned = testRoundTrip(cmd); assertNotNull(returned); assertNotNull(returned.getMainCommand()); assertTrue(returned.getMainCommand() instanceof SkipTaskCommand); assertEquals(Long.valueOf(1), returned.getTaskId()); assertNotNull(returned.getCommands()); assertEquals(2, returned.getCommands().size()); } @Test public void statusInCommandSerialization() throws Exception { Assume.assumeTrue(getType().equals(TestType.JAXB)); addClassesToSerializationContext(GetTaskAssignedAsPotentialOwnerCommand.class); List<Status> statuses = new ArrayList<Status>(); statuses.add(Status.Completed); statuses.add(Status.Exited); List<String> groupIds = new ArrayList<String>(); groupIds.add("team"); groupIds.add("region"); QueryFilter filter = new QueryFilter( 0, 0, "order", false); GetTaskAssignedAsPotentialOwnerCommand cmd = new GetTaskAssignedAsPotentialOwnerCommand( "user", groupIds, statuses, filter ); GetTaskAssignedAsPotentialOwnerCommand copyCmd = testRoundTrip(cmd); ComparePair.compareObjectsViaFields(cmd, copyCmd); } @Test public void jaxbContentTest() throws Exception { Assume.assumeFalse(getType().equals(TestType.YAML)); ContentImpl content = new ContentImpl(); content.setId(23); Map<String, Object> map = new HashMap<String, Object>(); map.put("life", new Integer(23)); map.put("sick", new Integer(45)); byte [] bytes = ContentMarshallerHelper.marshallContent(null, map, null); content.setContent(bytes); JaxbContent jaxbContent = new JaxbContent(content); JaxbContent copyJaxbContent = testRoundTrip(jaxbContent); ComparePair.compareObjectsViaFields(jaxbContent, copyJaxbContent); } @Test public void jaxbCommentTest() throws Exception { Assume.assumeFalse(getType().equals(TestType.YAML)); CommentImpl comment = new CommentImpl(); comment.setAddedAt(new Date()); comment.setAddedBy(new UserImpl("user")); comment.setId(23l); comment.setText("ILLUMINATI!"); JaxbComment jaxbComment = new JaxbComment(comment); assertEquals("added at", comment.getAddedAt(), jaxbComment.getAddedAt()); assertEquals("added by", comment.getAddedBy().getId(), jaxbComment.getAddedById()); assertEquals("added by", comment.getAddedBy().getId(), jaxbComment.getAddedBy().getId()); assertEquals("id", comment.getId(), jaxbComment.getId()); assertEquals("text", comment.getText(), jaxbComment.getText()); JaxbComment copyJaxbComment = testRoundTrip(jaxbComment); ComparePair.compareObjectsViaFields(jaxbComment, copyJaxbComment); } @Test public void jaxbI18NTextTest() throws Exception { Assume.assumeFalse(getType().equals(TestType.YAML)); I18NTextImpl textImpl = new I18NTextImpl(); textImpl.setId(1605l); textImpl.setLanguage("es-ES"); textImpl.setText("Quixote"); JaxbI18NText jaxbText = new JaxbI18NText(textImpl); assertEquals("id", textImpl.getId(), jaxbText.getId()); assertEquals("language", textImpl.getLanguage(), jaxbText.getLanguage()); assertEquals("text", textImpl.getText(), jaxbText.getText()); JaxbI18NText copyJaxbText = testRoundTrip(jaxbText); ComparePair.compareObjectsViaFields(jaxbText, copyJaxbText); List<I18NText> intList = new ArrayList<I18NText>(); intList.add(textImpl); List<JaxbI18NText> jaxbList = JaxbI18NText.convertListFromInterfaceToJaxbImpl(intList, I18NText.class, JaxbI18NText.class); jaxbText = jaxbList.get(0); assertEquals("id", textImpl.getId(), jaxbText.getId()); assertEquals("language", textImpl.getLanguage(), jaxbText.getLanguage()); assertEquals("text", textImpl.getText(), jaxbText.getText()); intList = JaxbI18NText.convertListFromJaxbImplToInterface(jaxbList); I18NText text = intList.get(0); assertEquals("id", text.getId(), jaxbText.getId()); assertEquals("language", text.getLanguage(), jaxbText.getLanguage()); assertEquals("text", text.getText(), jaxbText.getText()); } }