/*
* Copyright 2015 - 2017 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.kie.server.api.marshalling.xstream;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
import com.thoughtworks.xstream.mapper.MapperWrapper;
import org.drools.core.runtime.help.impl.XStreamXML;
import org.kie.server.api.commands.CallContainerCommand;
import org.kie.server.api.commands.CommandScript;
import org.kie.server.api.commands.CreateContainerCommand;
import org.kie.server.api.commands.DisposeContainerCommand;
import org.kie.server.api.commands.GetContainerInfoCommand;
import org.kie.server.api.commands.GetReleaseIdCommand;
import org.kie.server.api.commands.GetScannerInfoCommand;
import org.kie.server.api.commands.GetServerInfoCommand;
import org.kie.server.api.commands.ListContainersCommand;
import org.kie.server.api.commands.UpdateReleaseIdCommand;
import org.kie.server.api.commands.UpdateScannerCommand;
import org.kie.server.api.commands.optaplanner.CreateSolverCommand;
import org.kie.server.api.commands.optaplanner.DisposeSolverCommand;
import org.kie.server.api.commands.optaplanner.GetSolverCommand;
import org.kie.server.api.commands.optaplanner.GetSolverWithBestSolutionCommand;
import org.kie.server.api.commands.optaplanner.GetSolversCommand;
import org.kie.server.api.commands.optaplanner.SolvePlanningProblemCommand;
import org.kie.server.api.commands.optaplanner.TerminateSolverEarlyCommand;
import org.kie.server.api.marshalling.Marshaller;
import org.kie.server.api.marshalling.MarshallingFormat;
import org.kie.server.api.model.KieContainerResource;
import org.kie.server.api.model.KieContainerResourceFilter;
import org.kie.server.api.model.KieContainerResourceList;
import org.kie.server.api.model.KieContainerStatus;
import org.kie.server.api.model.KieContainerStatusFilter;
import org.kie.server.api.model.KieScannerResource;
import org.kie.server.api.model.KieServerInfo;
import org.kie.server.api.model.ReleaseId;
import org.kie.server.api.model.ReleaseIdFilter;
import org.kie.server.api.model.ServiceResponse;
import org.kie.server.api.model.ServiceResponsesList;
import org.kie.server.api.model.dmn.DMNContextKS;
import org.kie.server.api.model.dmn.DMNDecisionInfo;
import org.kie.server.api.model.dmn.DMNDecisionResultKS;
import org.kie.server.api.model.dmn.DMNMessageKS;
import org.kie.server.api.model.dmn.DMNModelInfo;
import org.kie.server.api.model.dmn.DMNModelInfoList;
import org.kie.server.api.model.dmn.DMNNodeStub;
import org.kie.server.api.model.dmn.DMNResultKS;
import org.kie.server.api.model.instance.SolverInstance;
import org.optaplanner.persistence.xstream.api.score.AbstractScoreXStreamConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
public class XStreamMarshaller
implements Marshaller {
private static final Logger logger = LoggerFactory.getLogger(XStreamMarshaller.class);
protected XStream xstream;
protected ClassLoader classLoader;
protected Map<String, Class> classNames = new HashMap<String, Class>();
// Optional marshaller extensions to handle new types / configure custom behavior
private static final List<XStreamMarshallerExtension> EXTENSIONS;
static {
logger.debug("XStreamMarshaller extensions init");
ServiceLoader<XStreamMarshallerExtension> plugins = ServiceLoader.load(XStreamMarshallerExtension.class);
List<XStreamMarshallerExtension> loadedPlugins = new ArrayList<>();
plugins.forEach(plugin -> {
logger.info("XStreamMarshallerExtension implementation found: {}", plugin.getClass().getName());
loadedPlugins.add(plugin);
});
EXTENSIONS = Collections.unmodifiableList(loadedPlugins);
}
public XStreamMarshaller( Set<Class<?>> classes, final ClassLoader classLoader ) {
this.classLoader = classLoader;
buildMarshaller(classes, classLoader);
configureMarshaller(classes, classLoader);
// Extend the marshaller with optional extensions
EXTENSIONS.forEach(ext -> ext.extend(this));
}
protected void buildMarshaller( Set<Class<?>> classes, final ClassLoader classLoader ) {
this.xstream = XStreamXML.newXStreamMarshaller( new XStream( new PureJavaReflectionProvider() ) {
protected MapperWrapper wrapMapper(MapperWrapper next) {
return new MapperWrapper(chainMapperWrappers(new ArrayList<>(EXTENSIONS), next)) {
public Class realClass(String elementName) {
Class customClass = classNames.get(elementName);
if (customClass != null) {
return customClass;
}
return super.realClass(elementName);
}
};
}
});
}
private MapperWrapper chainMapperWrappers(List<XStreamMarshallerExtension> extensions, MapperWrapper last) {
if (extensions.isEmpty()) {
return last;
} else {
XStreamMarshallerExtension head = extensions.remove(0);
return head.chainMapperWrapper(chainMapperWrappers(extensions, last));
}
}
protected void configureMarshaller( Set<Class<?>> classes, final ClassLoader classLoader ) {
this.xstream.setClassLoader( classLoader );
AbstractScoreXStreamConverter.registerScoreConverters(xstream);
this.xstream.processAnnotations( CommandScript.class );
this.xstream.processAnnotations( CallContainerCommand.class );
this.xstream.processAnnotations( CreateContainerCommand.class );
this.xstream.processAnnotations( DisposeContainerCommand.class );
this.xstream.processAnnotations( GetContainerInfoCommand.class );
this.xstream.processAnnotations( GetScannerInfoCommand.class );
this.xstream.processAnnotations( UpdateScannerCommand.class );
this.xstream.processAnnotations( GetReleaseIdCommand.class );
this.xstream.processAnnotations( UpdateReleaseIdCommand.class );
this.xstream.processAnnotations( GetServerInfoCommand.class );
this.xstream.processAnnotations( ListContainersCommand.class );
this.xstream.processAnnotations( ServiceResponsesList.class );
this.xstream.processAnnotations( ServiceResponse.class );
this.xstream.processAnnotations( KieContainerResourceList.class );
this.xstream.processAnnotations( KieContainerResource.class );
this.xstream.processAnnotations( ReleaseId.class );
this.xstream.processAnnotations( KieContainerStatus.class );
this.xstream.processAnnotations( KieScannerResource.class );
this.xstream.processAnnotations( KieServerInfo.class );
this.xstream.processAnnotations( ReleaseIdFilter.class );
this.xstream.processAnnotations( KieContainerStatusFilter.class );
this.xstream.processAnnotations( KieContainerResourceFilter.class );
this.xstream.processAnnotations( SolverInstance.class );
this.xstream.processAnnotations( CreateSolverCommand.class );
this.xstream.processAnnotations( DisposeSolverCommand.class );
this.xstream.processAnnotations( GetSolverWithBestSolutionCommand.class );
this.xstream.processAnnotations( GetSolversCommand.class );
this.xstream.processAnnotations( GetSolverCommand.class );
this.xstream.processAnnotations( SolvePlanningProblemCommand.class );
this.xstream.processAnnotations( TerminateSolverEarlyCommand.class );
this.xstream.processAnnotations( DMNContextKS.class );
this.xstream.processAnnotations( DMNResultKS.class );
this.xstream.processAnnotations( DMNNodeStub.class );
this.xstream.processAnnotations( DMNMessageKS.class );
this.xstream.processAnnotations( DMNDecisionResultKS.class);
this.xstream.processAnnotations( DMNModelInfoList.class );
this.xstream.processAnnotations( DMNModelInfo.class );
this.xstream.processAnnotations( DMNDecisionInfo.class);
if (classes != null) {
for (Class<?> clazz : classes) {
this.xstream.processAnnotations( clazz );
this.classNames.put(clazz.getName(), clazz);
}
}
}
@Override
public String marshall(Object objectInput) {
return xstream.toXML( objectInput );
}
@Override
public <T> T unmarshall(String input, Class<T> type) {
return (T) xstream.fromXML( input );
}
@Override
public void dispose() {
// nothing to do
}
@Override
public MarshallingFormat getFormat() {
return MarshallingFormat.XSTREAM;
}
@Override
public String toString() {
return "Marshaller{ XSTREAM }";
}
@Override
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
this.xstream.setClassLoader( classLoader );
}
@Override
public ClassLoader getClassLoader() {
return classLoader;
}
public XStream getXstream() {
return xstream;
}
}