How to generate XSDs from java beans having java.time.LocalDate? - java

I'm trying to use jaxb2-maven-plugin to generate xsd from java beans.
But I constantly get the following error:
java.time.LocalDate is a non-static inner class, and JAXB can't handle those.
this problem is related to the following location:
at java.time.LocalDate (Unknown Source)
The bean is like:
public class MyBean {
private LocalDate date;
}
I therefore created a jaxb mapping file and an adapter converting between java.time.LocalDate and xs:date. But the error just remains the same.
public class LocalDateAdapter {
private static DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyyMMdd");
public static LocalDate unmarshal(String v) throws Exception {
return fmt.parseLocalDate(v);
}
public static String marshal(LocalDate v) throws Exception {
return v.toString("yyyyMMdd");
}
}
src/main/resources/xsdbindings.xml:
<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
version="2.1">
<globalBindings>
<javaType name="java.time.LocalDate" xmlType="xs:date"
parseMethod="my.path.LocalDateAdapter.unmarshal"
printMethod="my.path.LocalDateAdapter.marshal"
/>
</globalBindings>
</bindings>
maven pom.xml
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.3.1</version>
<executions>
<execution>
<id>schemagen</id>
<goals>
<goal>schemagen</goal>
</goals>
</execution>
</executions>
<configuration>
<bindingFiles>xsdbindings.jaxb</bindingFiles>
<verbose>true</verbose>
</configuration>
</plugin>
</plugins>
</build>
</project>

Related

openapi codegen - generating List<byte[]> instead of byte[]

I have an application with the following structure (simplified for brevity):
project structure
myApp
src/main/java/
com.something
controllers
UserApi
UserController
model
UpsertRequest
pom.xml
myApp-generated-client
target
pom.xml
UserApi
#PutMapping(USER_UPSERT_PATH)
UpsertResponse userUpsert(
#RequestBody UpsertRequest upsertRequest,
#RequestHeader(name = COMPRESSED_DATA, required = false) byte[] compressedData);
pom.xml (myApp):
<plugin>
<groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>3.1.8</version>
<configuration>
<skipSwaggerGeneration>false</skipSwaggerGeneration>
<apiSources>
<apiSource>
<schemes>http,https</schemes>
<swaggerDirectory>
${project.basedir}/../myApp-generated-client/
</swaggerDirectory>
<locations>
<location>com.something.controllers</location>
</locations>
<springmvc>true</springmvc>
<outputFormats>json</outputFormats>
<attachSwaggerArtifact>true</attachSwaggerArtifact>
</apiSource>
</apiSources>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
pom.xml (myApp-generated-client)
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.1.2</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>
${project.basedir}/swagger.json
</inputSpec>
<supportingFilesToGenerate>
ApiClient.java,JSON.java,OAuth.java,Authentication.java,ApiKeyAuth.java,HttpBasicAuth.java,ApiException.java,ApiResponse.java,Configuration.java,Pair.java,RFC3339DateFormat.java,StringUtil.java,CustomInstantDeserializer.java,Client.java
</supportingFilesToGenerate>
<skipValidateSpec>true</skipValidateSpec>
<apiPackage>com.something.api</apiPackage>
<generatorName>java</generatorName>
<templateDirectory>${project.basedir}/templates</templateDirectory>
<generateApiTests>false</generateApiTests>
<generateModelTests>false</generateModelTests>
<generateModels>true</generateModels>
<configOptions>
<library>jersey2</library>
</configOptions>
<typeMappings>Resource=File</typeMappings>
<importMappings>
<importMapping>UpsertRequest=com.something.model.upsertRequest</importMapping>
</importMappings>
<modelPackage>com.something.api</modelPackage>
<modelsToGenerate/>
</configuration>
</execution>
</executions>
</plugin>
The problem is after compiling, my generated client keeps using List<byte[]> instead of byte[] for the upsert endpoint:
public ApiResponse<UpsertResponse> userUpsertWithHttpInfo(List<byte[]> compressedData, UpsertRequest body) throws ApiException
I read here that
Arrays become lists when you use the Swagger Codegen tool to generate Java clients.
For example, a field of type String[] becomes List.
Is there any way of overcoming this limitation (i.e., some converter class or some annotation)? For that matter, is there any good explanations/documentation about how to use converters to resolve types?
Update:
After seeing this link, I added the following code and updated my generated-client pom, but am getting the same results:
package com.something.converter;
public class ByteArrayFixerModelConverter extends AbstractModelConverter {
public ByteArrayFixerModelConverter() {
super(Json.mapper());
}
#Override
public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
if (isByteArray(type)) {
//bypass the chain! It would convert the ByteArrayProperty to an Array of ByteArrayProperty (bug in ModelModifier I think)
return new ByteArraySchema();
}
return chain.hasNext()
? chain.next().resolve(type, context, chain)
: null;
}
private boolean isByteArray(AnnotatedType annotatedType) {
Type type = annotatedType.getType();
boolean ret = type instanceof Class && type == byte[].class;
if (!ret && type instanceof ArrayType) {
ArrayType at = (ArrayType) type;
JavaType contentType = at.getContentType();
if (contentType instanceof SimpleType) {
SimpleType st = (SimpleType) contentType;
ret = st.getRawClass() == byte.class;
}
}
return ret;
}
}
pom.xml (myApp-generated-client, updated)
<plugin>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<modelConverterClasses>com.something.converter.ByteArrayFixerModelConverter</modelConverterClasses>
</configuration>
</plugin>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
... //same as before
</plugin>

Jnlp running dependency library's functions

I have a SwingWorker which runs a dependency's functions
public class JarRunnerWorker extends SwingWorker<Void, Void> {
private JnlpApp jnlpApp;
public JarRunnerWorker(){}
#Override
protected Void doInBackground() {
try {
System.out.println("*** running jar ***");
jnlpApp = com.asd.bsign.App.startForJnlp();
jnlpApp.run();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void setThreadPillFalse(){
jnlpApp.setThreadPillFalse();
}
Maven dependency and build from pom.xml:
<dependency>
<groupId>com.asd.bsign</groupId>
<artifactId>bsignclient</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/libs
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>libs/</classpathPrefix>
<mainClass>com.bermuda.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
This function works well in IDE and in my jar(jar and libs are in the same folder).
Here is my jnlp file(all jars in libs are added):
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://localhost:8080/bsign/" href="bsign.jnlp">
<information>
<title>Jnlp Testing</title>
<vendor>bermuda</vendor>
<homepage href="http://localhost:8080/bsign/" />
<description>jnlp Testing</description>
</information>
<security>
<all-permissions/>
</security>
<resources>
<j2se version="1.6+" />
<jar href="bsignclient.jar" />
<jar href="libs/commons-discovery-0.2.jar" />
<jar href="libs/commons-io-2.8.0.jar" />
<jar href="libs/commons-logging-1.1.1.jar" />
.
.
.
</resources>
<application-desc main-class="com.bermuda.App" />
</jnlp>
When I run jnlp, jnlp executes System.out.println("*** running jar ***"); but it doesn't work, when I run setThreadPillFalse I get NullPointerException
We can think JnlpApp class like this:
public class JnlpApp implements Runnable{
private boolean pill;
public JnlpApp(){
pill = true;
}
#Override
public void run() {
while(pill){
System.out.println("Doing some job");
Thread.sleep(5000);
}
}
public void setThreadPillTrue(){
connectionMonitor.setPill(true);
}
public void setThreadPillFalse(){
connectionMonitor.setPill(false);
}
}
And startForJnlp creates a JnlpApp and returns it.
How can I run this function from jnlp?

Spring RestDocs generating pages for frontend

My spring RestApi Application successfully generates snippets using RestDoc but I'm not able use the snippets to automatically generate pages to run on frontend. http://localhost/docs returns 404 and no html is generated in static/docs/
so far my pom.xml looks like this
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
<outputDirectory>
${project.build.outputDirectory}/static/docs
</outputDirectory>
<attributes>
<snippets>${project.build.directory}/generated-snippets</snippets>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
My Junit Api Test looks like this
#AutoConfigureMockMvc
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = AppLoader.class)
public class ApiDocumentationJUnit5IntegrationTest {
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
private ObjectMapper objectMapper = new ObjectMapper();
private RestDocumentationResultHandler documentationHandler;
#Before
public void setUp() {
this.documentationHandler = document("{method-name}",
preprocessRequest(removeHeaders("Authorization")),
preprocessResponse(prettyPrint()));
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
#Test
public void responseCodeTest() throws Exception {
this.mockMvc.perform(
get("/cms/status")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isOk())
.andDo(
document("status")
);
}
#Test
public void retrieveDocumentTest() throws Exception {
this.mockMvc.perform(get("/api/active", 1L))
.andExpect(status().isOk()).andDo(
document("active")
);
}
}

Unable to copy custom Object to Apache-CXF generated Object using Dozzer Mapper

I am using Apache CXF (org.apache.cxf) maven plugin to auto generate Java files (wsdl2java) and want to copy the response object generated from the business logic to the auto generated file generated by Apache CXF using Dozer mapper. But Dozer mapper is unable to copy list object from source to target as setter method of list is not generated using the CXF plugin.
XSD File:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://com/test/ram/webservices/customer" targetNamespace="http://com/test/ram/webservices/customer" elementFormDefault="qualified">
<xs:element name="customerRequest" type="tns:customerRequestType" />
<xs:complexType name="customerRequestType">
<xs:sequence>
<xs:element name="CustomerID" type="xs:string" minOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:element name="customerResponse" type="tns:customerResponseType" />
<xs:complexType name="customerResponseType">
<xs:sequence>
<xs:element name="TemplateList" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="Template" type="xs:string" minOccurs="0" maxOccurs="unbounded" >
<xs:annotation>
<xs:documentation>
TemplateList.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
pom.xml
org.apache.cxf plugin to generate wsdl2java
Custom Customer class:
public class customerResponse {
private customerResponse.TemplateList templateList;
public customerResponse .TemplateList getTemplateList() {
return templateList;
}
public void setTemplateList(customerResponse .TemplateList value) {
this.templateList = value;
}
public static class TemplateList {
private List<String> template;
public List<String> getTemplate() {
if (template == null) {
template = new ArrayList<String>();
}
return this.template;
}
public void setTemplate(List<String> tmp) {
template=tmp
}
}
}
CustomerResponse object auto generated by CXF plugin
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "customerResponse", propOrder = {
"templateList"
})
public class customerResponse {
#XmlElement(name = "TemplateList", required = true)
protected customerResponse.TemplateList templateList;
public customerResponse .TemplateList getTemplateList() {
return templateList;
}
public void setTemplateList(customerResponse .TemplateList value) {
this.templateList = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"template"
})
public static class TemplateList {
#XmlElement(name = "Template")
protected List<String> template;
public List<String> getTemplate() {
if (template == null) {
template = new ArrayList<String>();
}
return this.template;
}
}
}
Dozer mapper unable to copy TemplateList.
customerResponse response = dozerMapper.map(customerResponse, customerResponse.class);
So, is there a way to copy the list object using Dozer mapper in the absence of setter method in the auto generated java class?
You can use JAXB2 Setters Plugin which generates setter methods.
The plugin is activated by the -Xsetters command line option.
You can also use the -Xsetters-mode=accessor or -Xsetters-mode=direct options to configure the generation modes.
Refer below maven example to configure this plugin
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version><!-- Current version --></version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<args>
<arg>-XtoString</arg>
<arg>-Xequals</arg>
<arg>-XhashCode</arg>
<arg>-Xcopyable</arg>
<arg>-Xmergeable</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version><!-- Current version --></version>
</plugin>
</plugins>
</configuration>
</plugin>
Also add below dependency in your pom.xml because generated code depends on it.
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics-runtime</artifactId>
<version><!-- Current version --></version>
</dependency>
Refer this link for more help. Also, there is a user guide which might be helpful.
I got solution from following Reference Link now i can see generated java class has setter method for list.
https://gist.github.com/meiwin/2779731
https://github.com/highsource/hyperjaxb3-support/issues/4
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>moc.scodma.tsacmoc.mra</groupId>
<artifactId>mra</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>mra-messaging-schema</artifactId>
<properties>
<src.generated.dir>src/main/java</src.generated.dir>
<artifact.cxf.version>3.1.6</artifact.cxf.version>
<xerces.version>2.11.0</xerces.version>
<inbound.wsdl>src/main/resources/wsdl/inbound/tsacmocService.wsdl</inbound.wsdl>
<inbound.wsdl.location>classpath:resources/wsdl/inbound/tsacmocService.wsdl</inbound.wsdl.location>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.cxf.xjc-utils</groupId>
<artifactId>cxf-xjc-runtime</artifactId>
<version>3.0.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${artifact.cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${src.generated.dir}</sourceRoot>
<defaultOptions>
<noAddressBinding>true</noAddressBinding>
<faultSerialVersionUID>3105839350746982386</faultSerialVersionUID>
</defaultOptions>
<wsdlOptions>
<wsdlOption>
<wsdl>${inbound.wsdl}</wsdl>
<wsdlLocation>${inbound.wsdl.location}</wsdlLocation>
<serviceName>mraService</serviceName>
<extraargs>
<extraarg>-xjc-Xts</extraarg>
<extraarg>-xjc-Xcollection-setter-injector</extraarg>
<extraarg>-client</extraarg>
<extraarg>-verbose</extraarg>
<extraarg>-p</extraarg>
<extraarg>http://tsacmoc.ent.moc/mra/=moc.ent.tsacmoc.mra</extraarg>
<extraarg>-p</extraarg>
<extraarg>http://tsacmoc.ent.moc/mocmonheader/=moc.ent.tsacmoc.mocmonheader</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>${xerces.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf.xjcplugins</groupId>
<artifactId>cxf-xjc-ts</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>net.java.dev.vcc.thirdparty</groupId>
<artifactId>collection-setter-injector</artifactId>
<version>0.5.0-1</version>
</dependency>
<dependency>
<groupId>moc.sun.xml.bind</groupId>
<artifactId>jaxb-xjc</artifactId>
<version>2.1.8</version>
</dependency>
<dependency>
<groupId>moc.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.1.8</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
java :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"template"
})
public static class TemplateList {
#XmlElement(name = "Template")
protected List<String> template;
/**
* Gets the value of the template property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the template property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getTemplate().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {#link String }
*
*
*/
public List<String> getTemplate() {
if (template == null) {
template = new ArrayList<String>();
}
return this.template;
}
/**
* Sets the value of the template property.
*
* #param template
* allowed object is
* {#link String }
*
*/
public void setTemplate(List<String> template) {
this.template = template;
}
}

CXF service proxies cause NullPointerException when passed null array

Via using CXF 2.7.11 via Maven with the cxf-java2ws-plugin and cxf-codegen-plugin, a WSDL gets generated from a web service class and from that WSDL proxy classes are generated which then get used in various clients to connect to the actual, live server.
There is a problem with these generated service proxies in that they cause a NullPointerException when passed a null array.
The web service is something like this:
public class MyService {
public SomeResult someMethod(String param, String[] otherParams) {
if (otherParams == null){
... etc etc...
}
}
}
A generated WSDL fragment:
<xs:complexType name="someMethod">
<xs:sequence>
<xs:element minOccurs="0" name="param1" type="xs:string"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="otherParams" type="xs:string"/>
</xs:sequence>
</xs:complexType>
And finally a generated service proxy fragment :
public class SomeMethod
{
protected String param1;
protected String[] otherParams;
...
public String[] getOtherParams()
{
// Good, defensive code used here
if (this.otherParams == null)
{
return new String[0];
}
String[] retVal = new String[this.otherParams.length];
System.arraycopy(this.otherParams, 0, retVal, 0, this.otherParams.length);
return (retVal);
}
...
public void setOtherParams(String[] values)
{
// Complete lack of defensive code here!
int len = values.length;
this.otherParams = ((String[]) new String[len]);
for (int i = 0; (i < len); i++)
{
this.otherParams[i] = values[i];
}
}
public String setOtherParams(int idx,
String value)
{
// And here
return this.otherParams[idx] = value;
}
}
pom.xml fragment for WSDL generation:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-java2ws-plugin</artifactId>
<version>2.7.11</version>
<executions>
<execution>
<id>generate-wsdl-MyService</id>
<phase>process-classes</phase>
<goals>
<goal>java2ws</goal>
</goals>
<configuration>
<className>com.somewhere.services.MyService</className>
<serviceName>MyService</serviceName>
<genWsdl>true</genWsdl>
</configuration>
</execution>
</executions>
<plugin>
pom.xml fragment for proxy generation from above WSDL:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>2.7.11</version>
<executions>
<execution>
<id>generate-service-proxies</id>
<phase>generate-sources</phase>
<goals>
<goal>wsdl2java</goal>
</goals>
<configuration>
<defaultOptions>
<bindingFiles>
<bindingFile>some/location/jaxws-binding.xjb</bindingFile>
<bindingFile>some/location/jaxb-binding.xjb</bindingFile>
</bindingFiles>
<extraargs>
<extraarg>-fe</extraarg>
<extraarg>jaxws21</extraarg>
<extraarg>-xjc-npa</extraarg>
</extraargs>
</defaultOptions>
<wsdlOptions>
<wsdlOption>
<wsdl>some/location/generated/wsdl/MyService.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
</execution>
</executions>
</plugin>
How do I get CXF to generate service proxies that cope with clients wishing to pass a null array? (Not an empty array or array containing nulls, an actual null array). Do I need "otherParams" declared as "nillable=true" in the WSDL? If so, how?
I have tried many annotations and JAXB/JAXWS options and can't seem to get the desired behaviour.

Categories

Resources