Skip to content

Commit

Permalink
[FIX] custom fix when xml adapter has names (#3052)
Browse files Browse the repository at this point in the history
  • Loading branch information
elguardian committed Aug 1, 2024
1 parent babb3f0 commit d909c43
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class KieServerConstants {
public static final String KIE_SERVER_MODE = "org.kie.server.mode";
public static final String KIE_SERVER_INCLUDE_STACKTRACE = "org.kie.server.stacktrace.included";
public static final String KIE_SERVER_STRICT_ID_FORMAT = "org.kie.server.strict.id.format";
public static final String JSON_HANDLE_XML_ANY_ELEMENTS_NAMES = "org.kie.server.strict.json.xmlanyelements";
public static final String KIE_SERVER_STRICT_JAVABEANS_SERIALIZERS = "org.kie.server.strict.javaBeans.serializers";
public static final String KIE_SERVER_STRICT_JAXB_FORMAT = "org.kie.server.strict.jaxb.format";
public static final String KIE_SERVER_IMAGESERVICE_MAX_NODES = "org.kie.server.service.image.max_nodes";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import java.util.regex.Pattern;

import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
Expand Down Expand Up @@ -99,15 +101,15 @@ public class JSONMarshaller implements Marshaller {
private static final Logger logger = LoggerFactory.getLogger(JSONMarshaller.class);

private static final boolean STRICT_ID_FORMAT = Boolean.getBoolean(KieServerConstants.KIE_SERVER_STRICT_ID_FORMAT);
private boolean jsonHandleXmlAnyElementsNames = Boolean.getBoolean(KieServerConstants.JSON_HANDLE_XML_ANY_ELEMENTS_NAMES);
private final boolean STRICT_JAVABEANS_SERIALIZERS = Boolean.getBoolean(KieServerConstants.KIE_SERVER_STRICT_JAVABEANS_SERIALIZERS);

private static final String FIELDS = "fields";
private static final String NOT_NULL = "not_null";
public static final String FIELDS = "fields";
public static final String NOT_NULL = "not_null";

private boolean formatDate;
private String dateFormatStr = System.getProperty("org.kie.server.json.date_format", "yyyy-MM-dd'T'hh:mm:ss.SSSZ");


private boolean useStrictJavaBeans;
private boolean fallbackClassLoaderEnabled = Boolean.parseBoolean(System.getProperty("org.kie.server.json.fallbackClassLoader.enabled", "false"));

Expand All @@ -129,7 +131,7 @@ public static class JSONContext {
private boolean stripped;

private boolean wrap;

private boolean writeNull;

public JSONContext() {
Expand Down Expand Up @@ -182,8 +184,6 @@ public void setWriteNull(boolean writeNull) {
// Optional Marshaller Extension to handle new types
private static final List<JSONMarshallerExtension> EXTENSIONS;



// Load Marshaller Extension
static {
logger.info("Marshaller extensions init");
Expand Down Expand Up @@ -224,7 +224,7 @@ private static CNFEBehavior getCNFEBehavior() {
return CNFEBehavior.valueOf(cnfeBehaviorValue);
} catch (IllegalArgumentException iae) {
throw new MarshallingException(cnfeBehaviorValue + " is not supported for " + KieServerConstants.JSON_CUSTOM_OBJECT_DESERIALIZER_CNFE_BEHAVIOR +
". Please choose from " + Arrays.asList(CNFEBehavior.values()).toString(), iae);
". Please choose from " + Arrays.asList(CNFEBehavior.values()).toString(), iae);
}
}

Expand Down Expand Up @@ -301,6 +301,7 @@ public boolean useForType(JavaType t) {
}
return false;
}

};
typer = typer.init(JsonTypeInfo.Id.CLASS, null);
typer = typer.inclusion(JsonTypeInfo.As.WRAPPER_OBJECT);
Expand Down Expand Up @@ -403,8 +404,7 @@ public String marshall(Object input, Map<String, Object> parameters) {
if (parameters.containsKey(MARSHALLER_PARAMETER_STRICT)) {
jsonContext.get().setWrap(Boolean.parseBoolean((String) parameters.get(MARSHALLER_PARAMETER_STRICT)));
}
if (NOT_NULL.equals(parameters.get(FIELDS)))
{
if (NOT_NULL.equals(parameters.get(FIELDS))) {
jsonContext.get().setWriteNull(false);
}
return marshall(input);
Expand All @@ -416,7 +416,7 @@ public String marshall(Object input, Map<String, Object> parameters) {
@Override
public String marshall(Object objectInput) {
try {
return getMapper(objectMapper,notNullObjectMapper).writeValueAsString(wrap(objectInput));
return getMapper(objectMapper, notNullObjectMapper).writeValueAsString(wrap(objectInput));
} catch (IOException e) {
throw new MarshallingException("Error marshalling input", e);
}
Expand All @@ -432,7 +432,6 @@ public byte[] marshallAsBytes(Object objectInput) {

}


@Override
public <T> T unmarshall(String serializedInput, Class<T> type) {

Expand Down Expand Up @@ -503,14 +502,33 @@ public ExtendedJaxbAnnotationIntrospector(List<NamedType> customClasses, ObjectM

@Override
public List<NamedType> findSubtypes(Annotated a) {
List<NamedType> base = super.findSubtypes(a);

List<NamedType> complete = new ArrayList<NamedType>();
if (base != null) {
complete.addAll(base);
}
if (customClasses != null) {
complete.addAll(customClasses);
for (NamedType namedType : customClasses) {
Class<?> clazz = namedType.getType();
if (!a.getRawType().equals(clazz) && a.getRawType().isAssignableFrom(clazz)) {
complete.add(namedType);
}
}
}

XmlElements elements = findAnnotation(XmlElements.class, a, false, false, false);
if (elements != null) {
for (XmlElement elem : elements.value()) {
if (!jsonHandleXmlAnyElementsNames && customClasses.contains(new NamedType(elem.type(), elem.type().getSimpleName()))) {
continue;
}
String name = elem.name();
if (MARKER_FOR_DEFAULT.equals(name)) {
name = null;
}
complete.add(new NamedType(elem.type(), name));
}
} else {
List<NamedType> base = super.findSubtypes(a);
if (base != null) {
complete.addAll(base);
}
}
return complete;
}
Expand Down Expand Up @@ -701,7 +719,7 @@ public CustomObjectSerializer(ObjectMapper customObjectMapper) {

@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeRawValue(getMapper(customObjectMapper,notNullObjectMapper).writeValueAsString(value));
jgen.writeRawValue(getMapper(customObjectMapper, notNullObjectMapper).writeValueAsString(value));
}
}

Expand All @@ -720,17 +738,17 @@ public void serialize(Object value, JsonGenerator jgen, SerializerProvider provi
String className = value.getClass().getName();

if (value instanceof Collection) {
String collectionJson = writeCollection((Collection) value, getMapper(customObjectMapper,notNullObjectMapper));
String collectionJson = writeCollection((Collection) value, getMapper(customObjectMapper, notNullObjectMapper));
jgen.writeRawValue(collectionJson);
} else if (value instanceof Map) {
String mapJson = writeMap((Map) value, getMapper(customObjectMapper,notNullObjectMapper));
String mapJson = writeMap((Map) value, getMapper(customObjectMapper, notNullObjectMapper));
jgen.writeRawValue(mapJson);
} else if (value instanceof Object[] || value.getClass().isArray()) {
String arrayJson = writeArray((Object[]) value, getMapper(customObjectMapper,notNullObjectMapper));
String arrayJson = writeArray((Object[]) value, getMapper(customObjectMapper, notNullObjectMapper));
jgen.writeRawValue(arrayJson);
} else {

String json = getMapper(customObjectMapper,notNullObjectMapper).writeValueAsString(value);
String json = getMapper(customObjectMapper, notNullObjectMapper).writeValueAsString(value);

// don't wrap java and javax classes as they are always available, in addition avoid double wrapping
if (!className.startsWith("java.") && !className.startsWith("javax.") && !json.contains(className)) {
Expand Down Expand Up @@ -837,14 +855,11 @@ private String writeCollection(Collection<?> collection, ObjectMapper customObje
return builder.toString();
}
}

private ObjectMapper getMapper(ObjectMapper alwaysMapper, ObjectMapper notNullMapper)
{

private ObjectMapper getMapper(ObjectMapper alwaysMapper, ObjectMapper notNullMapper) {
return jsonContext.get().isWriteNull() ? alwaysMapper : notNullMapper;
}



class CustomObjectDeserializer extends UntypedObjectDeserializer {

private final Pattern VALID_JAVA_IDENTIFIER = Pattern.compile("(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*");
Expand Down Expand Up @@ -971,6 +986,7 @@ public Object wrapCustomObject(Map<String, Object> result) {
}
return result;
}

private Object[] toArray(Object element) {
if (element != null) {

Expand Down Expand Up @@ -1155,4 +1171,8 @@ public void setClassLoader(ClassLoader classLoader) {
public ClassLoader getClassLoader() {
return classLoader;
}

public void setXmlAnyElementsNames(boolean jsonHandleXmlAnyElementsNames) {
this.jsonHandleXmlAnyElementsNames = jsonHandleXmlAnyElementsNames;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,36 @@

package org.kie.server.api.marshalling.json;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.assertj.core.api.Assertions;
import org.drools.core.command.runtime.BatchExecutionCommandImpl;
import org.drools.core.command.runtime.rule.InsertObjectCommand;
import org.drools.core.util.IoUtils;
import org.junit.Test;
import org.kie.api.pmml.PMMLRequestData;
import org.kie.server.api.marshalling.Marshaller;
import org.kie.server.api.marshalling.MarshallerFactory;
import org.kie.server.api.marshalling.MarshallingFormat;
import org.kie.server.api.marshalling.objects.CustomPerson;
import org.kie.server.api.marshalling.objects.FreeFormItemType;
import org.kie.server.api.marshalling.objects.ItemsType;
import org.kie.server.api.marshalling.objects.StandardItemType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;

public class JSONMarshallerExtensionTest {
Expand Down Expand Up @@ -83,4 +95,61 @@ public void testObjectInsideCommand() {
assertEquals(50, result.getAge());

}

@Test
public void testPolymorphicInsideCommand() throws IOException {
Set<Class<?>> extraClasses = new HashSet<Class<?>>();
extraClasses.add(FreeFormItemType.class);
extraClasses.add(StandardItemType.class);
extraClasses.add(ItemsType.class);
Marshaller marshaller = MarshallerFactory.getMarshaller(extraClasses, MarshallingFormat.JSON, this.getClass().getClassLoader());

byte[] content = new byte[0];
String marshall = null;
URL uri = this.getClass().getClassLoader().getResource("poly_payload.json");
try (InputStream is = uri.openStream()) {
content = IoUtils.readBytesFromInputStream(is);
BatchExecutionCommandImpl command = marshaller.unmarshall(content, BatchExecutionCommandImpl.class);
marshall = marshaller.marshall(command);
}

BatchExecutionCommandImpl input = marshaller.unmarshall(content, BatchExecutionCommandImpl.class);
BatchExecutionCommandImpl output = marshaller.unmarshall(marshall, BatchExecutionCommandImpl.class);

ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(Include.NON_NULL);
ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter();

String inputStr = writer.writeValueAsString(input);
String outputStr = writer.writeValueAsString(output);
assertThat(inputStr).isEqualTo(outputStr);
}

@Test
public void testPolymorphicInsideCommandHandle() throws IOException {
Set<Class<?>> extraClasses = new HashSet<Class<?>>();
extraClasses.add(FreeFormItemType.class);
extraClasses.add(StandardItemType.class);
extraClasses.add(ItemsType.class);
Marshaller marshaller = MarshallerFactory.getMarshaller(extraClasses, MarshallingFormat.JSON, this.getClass().getClassLoader());
((JSONMarshaller) marshaller).setXmlAnyElementsNames(true);

byte[] content = new byte[0];
String marshall = null;
URL uri = this.getClass().getClassLoader().getResource("poly_payload.json");
try (InputStream is = uri.openStream()) {
content = IoUtils.readBytesFromInputStream(is);
BatchExecutionCommandImpl command = marshaller.unmarshall(content, BatchExecutionCommandImpl.class);
marshall = marshaller.marshall(command);
}

BatchExecutionCommandImpl input = marshaller.unmarshall(content, BatchExecutionCommandImpl.class);
BatchExecutionCommandImpl output = marshaller.unmarshall(marshall, BatchExecutionCommandImpl.class);

ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(Include.NON_NULL);
ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter();

String inputStr = writer.writeValueAsString(input);
String outputStr = writer.writeValueAsString(output);
assertThat(inputStr).isEqualTo(outputStr);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2024 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.
* 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.kie.server.api.marshalling.objects;

import java.io.Serializable;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "FreeFormItemType", propOrder = {
"itemValue"
})
public class FreeFormItemType implements Serializable, Cloneable
{

private final static long serialVersionUID = 1L;

@XmlElement(required = true)
protected String itemValue;

/**
* Gets the value of the itemValue property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getItemValue() {
return itemValue;
}

/**
* Sets the value of the itemValue property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setItemValue(String value) {
this.itemValue = value;
}

}
Loading

0 comments on commit d909c43

Please sign in to comment.