From d3da0838158ab497875efb5ef90bb9aad341b483 Mon Sep 17 00:00:00 2001 From: Felix Mayerhuber Date: Sun, 10 Mar 2013 17:00:41 +0100 Subject: [PATCH 1/2] [OPENENGSB-3539] added additional serializer to the JsonUtils class so that models can be deserilized --- .../services/internal/JsonSupportTest.java | 87 +++++++++++++++++++ .../services/internal/model/NullModel.java | 12 +++ .../services/internal/model/SubModel.java | 48 ++++++++++ .../org/openengsb/core/util/JsonUtils.java | 70 +++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 components/services/src/test/java/org/openengsb/core/services/internal/JsonSupportTest.java create mode 100644 components/services/src/test/java/org/openengsb/core/services/internal/model/SubModel.java diff --git a/components/services/src/test/java/org/openengsb/core/services/internal/JsonSupportTest.java b/components/services/src/test/java/org/openengsb/core/services/internal/JsonSupportTest.java new file mode 100644 index 000000000..b55298b7b --- /dev/null +++ b/components/services/src/test/java/org/openengsb/core/services/internal/JsonSupportTest.java @@ -0,0 +1,87 @@ +/** + * Licensed to the Austrian Association for Software Tool Integration (AASTI) + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. The AASTI licenses this file to you 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.openengsb.core.services.internal; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; +import org.openengsb.core.api.model.OpenEngSBModelEntry; +import org.openengsb.core.services.internal.model.NullModel; +import org.openengsb.core.services.internal.model.SubModel; +import org.openengsb.core.util.JsonUtils; +import org.openengsb.core.util.ModelUtils; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonSupportTest { + private ObjectMapper mapper; + + public JsonSupportTest() { + mapper = new ObjectMapper(); + } + + private NullModel createTestModel() { + NullModel model = new NullModel(); + model.setId(20); + model.setValue("test"); + SubModel sub1 = new SubModel(); + sub1.setId("sub1"); + sub1.setName("test1"); + SubModel sub2 = new SubModel(); + sub2.setId("sub2"); + sub2.setName("test2"); + model.setSubs(Arrays.asList(sub1, sub2)); + + ModelUtils.addOpenEngSBModelEntry(model, new OpenEngSBModelEntry("test", "test", String.class)); + ModelUtils.addOpenEngSBModelEntry(model, new OpenEngSBModelEntry("test2", "test2", String.class)); + return model; + } + + @Test(expected = IOException.class) + public void tryConvertJSONIntoModelWithoutAdditionalSerializer_shouldThrowException() throws Exception { + NullModel model = createTestModel(); + String result = mapper.writeValueAsString(model); + // the read value call without the additional serialzer throws an IOException because it can not + // deserialize the model tail + mapper.readValue(result, NullModel.class); + } + + @Test + public void tryConvertJSONIntoModelWithAdditionalSerializer_shouldThrowNoException() throws Exception { + NullModel model = createTestModel(); + String result = mapper.writeValueAsString(model); + + NullModel other = JsonUtils.convertObject(result, NullModel.class); + List entries = ModelUtils.getOpenEngSBModelTail(other); + assertThat(model.getId(), is(other.getId())); + assertThat(model.getValue(), is(other.getValue())); + assertThat(model.getSubs().size(), is(other.getSubs().size())); + assertThat(model.getSubs().get(0).getId(), is(other.getSubs().get(0).getId())); + assertThat(model.getSubs().get(0).getName(), is(other.getSubs().get(0).getName())); + assertThat(model.getSubs().get(1).getId(), is(other.getSubs().get(1).getId())); + assertThat(model.getSubs().get(1).getName(), is(other.getSubs().get(1).getName())); + assertThat(entries.size(), is(2)); + assertThat(entries.contains(new OpenEngSBModelEntry("test", "test", String.class)), is(true)); + assertThat(entries.contains(new OpenEngSBModelEntry("test2", "test2", String.class)), is(true)); + } +} diff --git a/components/services/src/test/java/org/openengsb/core/services/internal/model/NullModel.java b/components/services/src/test/java/org/openengsb/core/services/internal/model/NullModel.java index f6acba5f2..327eb42e8 100644 --- a/components/services/src/test/java/org/openengsb/core/services/internal/model/NullModel.java +++ b/components/services/src/test/java/org/openengsb/core/services/internal/model/NullModel.java @@ -16,6 +16,8 @@ */ package org.openengsb.core.services.internal.model; +import java.util.List; + import org.openengsb.core.api.Constants; import org.openengsb.core.api.model.annotation.Model; import org.openengsb.core.api.model.annotation.OpenEngSBModelId; @@ -27,6 +29,7 @@ public class NullModel { @OpenEngSBModelId private Integer id; private String value; + private List subs; public Integer getId() { return id; @@ -43,4 +46,13 @@ public String getValue() { public void setValue(String value) { this.value = value; } + + public List getSubs() { + return subs; + } + + public void setSubs(List subs) { + this.subs = subs; + } + } diff --git a/components/services/src/test/java/org/openengsb/core/services/internal/model/SubModel.java b/components/services/src/test/java/org/openengsb/core/services/internal/model/SubModel.java new file mode 100644 index 000000000..f3f92c347 --- /dev/null +++ b/components/services/src/test/java/org/openengsb/core/services/internal/model/SubModel.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Austrian Association for Software Tool Integration (AASTI) + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. The AASTI licenses this file to you 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.openengsb.core.services.internal.model; + +import org.openengsb.core.api.Constants; +import org.openengsb.core.api.model.annotation.Model; +import org.openengsb.core.api.model.annotation.OpenEngSBModelId; +import org.openengsb.labs.delegation.service.Provide; + +@Model +@Provide(context = Constants.DELEGATION_CONTEXT_MODELS) +public class SubModel { + @OpenEngSBModelId + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/components/util/src/main/java/org/openengsb/core/util/JsonUtils.java b/components/util/src/main/java/org/openengsb/core/util/JsonUtils.java index 57bca647e..a69d72c7b 100644 --- a/components/util/src/main/java/org/openengsb/core/util/JsonUtils.java +++ b/components/util/src/main/java/org/openengsb/core/util/JsonUtils.java @@ -17,9 +17,11 @@ package org.openengsb.core.util; +import java.io.IOException; import java.lang.reflect.Array; import java.util.List; +import org.openengsb.core.api.model.OpenEngSBModelEntry; import org.openengsb.core.api.remote.MethodCall; import org.openengsb.core.api.remote.MethodCallMessage; import org.openengsb.core.api.remote.MethodResult; @@ -27,10 +29,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector; import com.google.common.base.Preconditions; @@ -40,6 +47,25 @@ public final class JsonUtils { private static final ObjectMapper MAPPER = new ObjectMapper(); + static { + // adding the additional deserializer needed to deserialize models + MAPPER.registerModule(new SimpleModule().addDeserializer(Object.class, new OpenEngSBModelEntryDeserializer())); + } + + /** + * Converts an object in JSON format to the given class. Throws an IOException if the conversion could not be + * performed. + */ + public static T convertObject(String json, Class clazz) throws IOException { + try { + return MAPPER.readValue(json, clazz); + } catch (IOException e) { + String error = String.format("Unable to parse given json '%s' into class '%s'.", json, clazz.getName()); + LOGGER.error(error, e); + throw new IOException(error, e); + } + } + private static Object convertArgument(String className, Object arg) { try { Class type = findType(className); @@ -93,4 +119,48 @@ public static ObjectMapper createObjectMapperWithIntroSpectors() { private JsonUtils() { } + + /** + * The OpenEngSBModelEntryDeserializer class is needed in order to be able to transform the list of + * OpenEngSBModelEntry elements, which is contained in every model tail, from a JSON string into a list of actual + * elements. + */ + @SuppressWarnings("serial") + private static class OpenEngSBModelEntryDeserializer extends StdScalarDeserializer { + public OpenEngSBModelEntryDeserializer() { + super(OpenEngSBModelEntry.class); + } + + @Override + public OpenEngSBModelEntry deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + JsonToken token = jp.getCurrentToken(); + OpenEngSBModelEntry entry = new OpenEngSBModelEntry(); + if (token != JsonToken.START_OBJECT) { + return null; + } else { + // skip the JsonToken.START_OBJECT token + token = jp.nextValue(); + } + do { + if (token == JsonToken.END_OBJECT) { + return entry; + } else { + if (jp.getCurrentName().equals("key")) { + entry.setKey(jp.getValueAsString()); + } else if (jp.getCurrentName().equals("value")) { + entry.setValue(jp.getValueAsString()); + } else if (jp.getCurrentName().equals("type")) { + try { + entry.setType(findType(jp.getValueAsString())); + } catch (ClassNotFoundException e) { + LOGGER.error("Did not find class of type " + jp.getValueAsString(), e); + break; + } + } + } + token = jp.nextValue(); + } while (token != null); + return null; + } + } } From 92f0ad8396a17842575198fc6c6943361a0729cf Mon Sep 17 00:00:00 2001 From: Felix Mayerhuber Date: Sun, 10 Mar 2013 21:30:26 +0100 Subject: [PATCH 2/2] [OPENENGSB-3539] added additional integration test to check model tail support in the running environment --- .../org/openengsb/itests/exam/JMSPortIT.java | 33 +++++++++++++++++++ .../itests/util/AbstractRemoteTestHelper.java | 15 +++++++++ 2 files changed, 48 insertions(+) diff --git a/itests/src/test/java/org/openengsb/itests/exam/JMSPortIT.java b/itests/src/test/java/org/openengsb/itests/exam/JMSPortIT.java index 8c8230a26..225aa7472 100644 --- a/itests/src/test/java/org/openengsb/itests/exam/JMSPortIT.java +++ b/itests/src/test/java/org/openengsb/itests/exam/JMSPortIT.java @@ -45,11 +45,13 @@ import org.junit.runner.RunWith; import org.openengsb.core.api.AliveState; import org.openengsb.core.api.model.ConnectorDescription; +import org.openengsb.core.api.model.OpenEngSBModelEntry; import org.openengsb.core.api.remote.MethodResultMessage; import org.openengsb.core.api.remote.OutgoingPort; import org.openengsb.core.common.AbstractOpenEngSBService; import org.openengsb.core.util.DefaultOsgiUtilsService; import org.openengsb.core.util.JsonUtils; +import org.openengsb.core.util.ModelUtils; import org.openengsb.core.workflow.api.model.RuleBaseElementId; import org.openengsb.core.workflow.api.model.RuleBaseElementType; import org.openengsb.domain.example.ExampleDomain; @@ -218,6 +220,32 @@ public void testSendMethodWithModelAsParamter_shouldWork() throws Exception { assertThat(decryptedResult.contains("successful"), is(true)); assertThat(model.getResult(), is("successful")); } + + @Test + public void testSendMethodWithModelIncludingTailAsParamter_shouldWork() throws Exception { + ExampleDomain service = new DummyService("test"); + Hashtable properties = new Hashtable(); + properties.put(Constants.SERVICE_PID, "test"); + properties.put(Constants.SERVICE_RANKING, -1); + properties.put("location.root", new String[]{ "foo" }); + getBundleContext().registerService(ExampleDomain.class.getName(), service, properties); + + JmsTemplate template = prepareActiveMqConnection(); + String secureRequest = prepareRequest(METHOD_CALL_WITH_MODEL_INCLUDING_TAIL_PARAMETER, "admin", "password"); + SecretKey sessionKey = generateSessionKey(); + String encryptedMessage = encryptMessage(secureRequest, sessionKey); + + String result = sendMessage(template, encryptedMessage); + String decryptedResult = decryptResult(sessionKey, result); + + ObjectMapper mapper = new ObjectMapper(); + MethodResultMessage methodResult = mapper.readValue(decryptedResult, MethodResultMessage.class); + JsonUtils.convertResult(methodResult); + ExampleResponseModel model = (ExampleResponseModel) methodResult.getResult().getArg(); + + assertThat(decryptedResult.contains("successful with tail"), is(true)); + assertThat(model.getResult(), is("successful with tail")); + } private String sendMessage(final JmsTemplate template, final String msg) { String resultString = template.execute(new SessionCallback() { @@ -273,6 +301,11 @@ public AliveState getAliveState() { public ExampleResponseModel doSomethingWithModel(ExampleRequestModel model) { ExampleResponseModel response = new ExampleResponseModel(); response.setResult("successful"); + for (OpenEngSBModelEntry entry : ModelUtils.getOpenEngSBModelTail(model)) { + if (entry.getKey().equals("specialKey") && entry.getValue().equals("specialValue")) { + response.setResult("successful with tail"); + } + } return response; } } diff --git a/itests/src/test/java/org/openengsb/itests/util/AbstractRemoteTestHelper.java b/itests/src/test/java/org/openengsb/itests/util/AbstractRemoteTestHelper.java index 5d3fd699f..6ca3dfadf 100644 --- a/itests/src/test/java/org/openengsb/itests/util/AbstractRemoteTestHelper.java +++ b/itests/src/test/java/org/openengsb/itests/util/AbstractRemoteTestHelper.java @@ -117,6 +117,21 @@ public class AbstractRemoteTestHelper extends AbstractExamTestHelper { + " \"args\": [" + " { \"id\":10, \"name\":\"test\" } ]" + "}"; + + protected static final String METHOD_CALL_WITH_MODEL_INCLUDING_TAIL_PARAMETER = "" + + "{" + + " \"classes\": [" + + " \"org.openengsb.domain.example.model.ExampleRequestModel\"" + + " ]," + + " \"methodName\": \"doSomethingWithModel\"," + + " \"metaData\": {" + + " \"serviceId\": \"test\"" + + " }," + + " \"args\": [" + + " { \"id\":10, \"name\":\"test\", \"openEngSBModelTail\" :[{ " + + " \"key\":\"specialKey\", \"value\":\"specialValue\", \"type\":\"java.lang.String\" }] " + + " } ]" + + "}"; protected RuleManager ruleManager;