Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.67.x] [FIX] custom fix when xml adapter has names #3059

Open
wants to merge 1 commit into
base: 7.67.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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