CXF unmarshalling Date not accepting NULL value - web-services

I have a WSDL file containing -
<xs:complexType name="PNV">
<xs:sequence>
<xs:element name="dataType" type="xsd:int" />
<xs:element name="multiValued" type="xsd:boolean" />
<xs:element name="booleanValue" type="xsd:boolean" />
<xs:element name="dateValue" nillable="true" type="xsd:dateTime" />
..........
and it is converted to Java file like this -
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "PNV", propOrder = {
"dataType",
"multiValued",
"booleanValue",
"dateValue",
})
public class PNV
implements Serializable
{
private final static long serialVersionUID = 1L;
protected int dataType;
protected boolean multiValued;
protected boolean booleanValue;
#XmlElement(required = true, type = String.class, nillable = true)
#XmlJavaTypeAdapter(Adapter1 .class)
#XmlSchemaType(name = "dateTime")
protected Date dateValue;
}
binding file -
<jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema[#targetNamespace='http://changeservice.xxservices.service.xx']">
<jxb:globalBindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:javaType name="java.util.Date" xmlType="xs:dateTime"
parseMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.parseDateTime"
printMethod="org.apache.cxf.xjc.runtime.DataTypeAdapter.printDateTime"/>
<jxb:serializable uid="1"/>
</jxb:globalBindings>
</jaxws:bindings>
in my SOAPUI Test case I passed -
<dateValue nil="true"/>
but it gave me following error -
Caused by: java.lang.IllegalArgumentException:
at com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl$Parser.parseAndSetYear(XMLGregorianCalendarImpl.java:2891) ~[na:1.8.0_201]
at com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl$Parser.parse(XMLGregorianCalendarImpl.java:2777) ~[na:1.8.0_201]
at com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl.<init>(XMLGregorianCalendarImpl.java:436) ~[na:1.8.0_201]
at com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl.newXMLGregorianCalendar(DatatypeFactoryImpl.java:536) ~[na:1.8.0_201]
at javax.xml.bind.DatatypeConverterImpl._parseDateTime(DatatypeConverterImpl.java:422) ~[na:1.8.0_201]
at javax.xml.bind.DatatypeConverterImpl.parseDateTime(DatatypeConverterImpl.java:417) ~[na:1.8.0_201]
at javax.xml.bind.DatatypeConverter.parseDateTime(DatatypeConverter.java:327) ~[na:1.8.0_201]
at org.apache.cxf.xjc.runtime.DataTypeAdapter.parseDateTime(DataTypeAdapter.java:65) ~[cxf-xjc-runtime-3.3.1.jar:na]
at org.w3._2001.xmlschema.Adapter1.unmarshal(Adapter1.java:13) ~[classes/:na]
at org.w3._2001.xmlschema.Adapter1.unmarshal(Adapter1.java:1) ~[classes/:na]
at com.sun.xml.bind.v2.runtime.reflect.AdaptedAccessor.set(AdaptedAccessor.java:55) ~[jaxb-runtime-2.3.3.jar:2.3.3]
... 107 common frames omitted
I checked the date attribute in Adapter1 class and it is coming as blank instead of null so it's throwing an error. My Question is why it is not taking NULL value even though I am passing nil=true in my SOAPUI test case. why it is coming as blank? I also tried to completely remove this dateValue field from my input request and its still giving me same error for date field.
I also tried to use XMLGregorianCalendar as a type of dateValue but when I passed nil=true in SOAPUI it's giving me same error message.. Please help.

This link helped me:
https://blogs.oracle.com/rammenon/specifying-null-content-in-your-xml-document-using-nillable-and-xsi:nil
I added xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance in my SOAPUI test & added date Value as
<dateValue xsi:nil="true"/>

If you are not able to change wsdl file (in case in supplied by external system), you can override standard org.apache.cxf.xjc.runtime.DataTypeAdapter
Root of the issue is not null but empty string, coming as input parameter to the parseDateTime method.
create class similar to the "org.apache.cxf.xjc.runtime.DataTypeAdapter" with extra check for empty string
public class DateAdapter {
public static Date parseDateTime(String s) {
if (s == null||s.isEmpty()) {
return null;
}
return DatatypeConverter.parseDateTime(s).getTime();
}
public static String printDateTime(Date dt) {
if (dt == null) {
return null;
}
Calendar c = Calendar.getInstance();
c.setTime(dt);
return DatatypeConverter.printDateTime(c);
}
}
specify the new class in the binding file
<jaxws:bindings node="wsdl:definitions/wsdl:types/xs:schema[#targetNamespace='http://changeservice.xxservices.service.xx']">
<jxb:globalBindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:javaType name="java.util.Date" xmlType="xs:dateTime"
parseMethod="your_class_here.parseDateTime"
printMethod="your_class_here.printDateTime"/>
<jxb:serializable uid="1"/>
</jxb:globalBindings>
</jaxws:bindings>

Related

Replace string in log4j2 filename in RollingFileAppender

I am trying to replace a string in a environment variable that I use in the filename as shown below. But the output file is generated literally with '%replace'. Is it possible to do such a replacement? If not, any other options? Essentially, I want to replace 'abc' with blank in the environment variable ${current.env}
<RollingFile name="FileA"
fileName="${sys:file.path}/%replace{${sys:current.env}}{'abc'}{}-xyz.log"
filePattern="${sys:file.path}/%replace{${sys:current.env}}{'abc'}{}-xyz-%d{yyyyMMdd-HHmm}.log">
<PatternLayout>
<pattern>%m%n</pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="5" />
</RollingFile>
Here is some sample code showing how to create a custom lookup that is able to replace strings within the lookup result.
First the class that generates the logging:
package example;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class SomeClass {
private static final Logger log = LogManager.getLogger();
public static void main(String[] args){
log.info("Here's some info!");
log.error("Some error happened!");
}
}
Next the class that defines the custom lookup:
package example;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.StrLookup;
#Plugin(name = "sysWithReplace", category = "Lookup")
public class SystemLookupWithReplace implements StrLookup{
/**
* Lookup the value for the key.
* #param key the key to be looked up, may be null
* #return The value for the key.
*/
public String lookup(String key) {
if(key == null){
return null;
}
if(!key.contains(";")){
return System.getProperty(key);
}
String[] params = key.split(";");
String value = System.getProperty(params[0]);
if(params.length >= 2 && value != null){
if(params.length < 3){
return value.replace(params[1], "");
}
return value.replace(params[1], params[2]);
}
return value;
}
/**
* Lookup the value for the key using the data in the LogEvent.
* #param event The current LogEvent.
* #param key the key to be looked up, may be null
* #return The value associated with the key.
*/
public String lookup(LogEvent event, String key) {
return lookup(key);
}
}
Here is a sample log4j2.xml config file that uses the custom lookup:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %c ${sysWithReplace:os.name;dows 10;doze} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" level="info" />
</Root>
</Loggers>
</Configuration>
and finally the logging output (I'm running this on Windows 10 so it replaces the "dows 10" with "doze":
14:30:26.580 [main] INFO example.SomeClass Windoze - Here's some info!
14:30:26.581 [main] ERROR example.SomeClass Windoze - Some error happened!

Why does CXF return an empty array rather than a null?

This is related to my other question about CXF proxies not accepting nulls.
From this auto-generated WSDL:
<xs:complexType name="someMethod2">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" nillable="true" name="params2" type="xs:string"/>
</xs:sequence>
</xs:complexType>
A proxy like this is generated:
public class SomeMethod2
{
protected String[] params2;
...
public String[] getParams2()
{
// CXF replaces null with an empty array
if (this.params2== null)
{
return new String[0];
}
String[] retVal = new String[this.params2.length];
System.arraycopy(this.params2, 0, retVal, 0, this.params2.length);
return (retVal);
}
...
}
As you can see, in "getOtherParams" the null gets switched for an empty String array. This causes issues in legacy code which has dependent on receiving a hard-null. Why does CXF doe this and is there any way to configure CXF/JAXB/JAXWS to force the return of an actual null?

Adding persistence to a REST application error

How can I user persistency in my REST application?
I want to use #Inject and #PersistenceUnit annotation to instantiate my needed PersistencyManager object,
by when I call the persistency functionality by REST service I get an error:
[2014-03-15 05:05:45,472] Artifact CubieboardGPIO:war: Error during artifact deployment. See server log for details.
[2014-03-15 05:05:45,473] Artifact CubieboardGPIO:war: javax.ejb.EJBException: The bean encountered a non-application exception; nested exception is:
org.apache.openejb.OpenEJBRuntimeException: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/]]
This is my Rest:
#Inject
private PersistenceManager persistenceManager;
#GET
#Path("/activate")
public String activate(#QueryParam("pin") int pin) {
persistenceManager = new PersistenceManager();
// URI : /cubieboard/gpio/activate?pin=67
String[] messages = _activate(pin);
StringBuilder sb = new StringBuilder();
sb.append(ConfigurationManager.getConfiguration("ACTIVATE_MESSAGE")+"\n");
sb.append("Persistence:\t"); sb.append(messages[0]); sb.append("\n");
sb.append("Terminal Response:\t"); sb.append(messages[1]); sb.append("\n");
return sb.toString();
}
This is PersisteneManager:
#Stateless
public class PersistenceManager {
#PersistenceUnit(name = "cubieDB")
private static EntityManagerFactory factory;
private EntityManager em;
public PersistenceManager(){
// factory = Persistence.createEntityManagerFactory(ConfigurationManager.getConfiguration("PERSISTENCE_UNIT_NAME"));
// factory = Persistence.createEntityManagerFactory("cubieDB");
em = factory.createEntityManager();
}
public List<Operation> getAllOperations() {
Query q = em.createQuery("SELECT op FROM Operation op");
List<Operation> ops = q.getResultList();
return ops;
}
public void persist(Operation operation) {
em.getTransaction().begin();
em.persist(operation);
em.getTransaction().commit();
}
public void deleteAll(){
em.getTransaction().begin();
Query q = em.createNativeQuery("DELETE FROM Operation");
q.executeUpdate();
em.getTransaction().commit();
}
#Override
protected void finalize() throws Throwable {
super.finalize();
em.close();
}
}
And this is my persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="cubieDB" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>com.sakhoshdel.cubieboard.gpio.persistence.Operation</class>
<properties>
<!--<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>-->
<property name="eclipselink.ddl-generation" value="create-tables"/>
<!--<property name="javax.persistence.schema-generation-action" value="drop-and-create"/>-->
<!--<property name="javax.persistence.schema-generation-target" value="database-and-scripts"/>-->
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="javax.persistence.jdbc.url" value="jdbc:derby:cubieDB;create=true" />
</properties>
</persistence-unit>
</persistence>
Also when I call the persistence functionality from a normal class it runs. but when I want to deploy it I get error. I am using TomEE for application server.
Thank you
Well ejb constructor shouldnt be used (shouldnt even be called in last versions). Injection of static field is not recommanded neither.
Finally maybe just use a jta persistence unit and remove the em.getTransaction calls.
Side note: tomee comes with openjpa so you need to provide eclipselink if you want it

null values for java.util.Date input/output in web service methods

I have a webservice created with jax-ws and netbeans 7's wizard (this is my first time) .
to use java.util.Date instead of XmlGregorianCalendar, I have modified the client's webservice with this xml :
<?xml version="1.0" encoding="UTF-8"?>
<jaxws:bindings node="wsdl:definitions/wsdl:types/xsd:schema"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<jaxb:globalBindings>
<jaxb:serializable/>
<jaxb:javaType name="java.util.Date" xmlType="xsd:dateTime"/>
</jaxb:globalBindings>
</jaxws:bindings>
Date is now used instead of XmlGregorianCalendar and an Adapter1 is generated :
public class Adapter1
extends XmlAdapter<String, Date>
{
public Date unmarshal(String value) {
return new Date(value);
}
public String marshal(Date value) {
if (value == null) {
return null;
}
return value.toString();
}
}
When i add a system.out.println in the adapter i see that the Date is received/sent to the server but if i use ws's methods i get always null on Date field/parameters .
Thanks .
The XmlAdapter that is generated is not going to perform the desired conversions. The default XmlAdapter expects the following to work:
Foo foo1 = new Foo(foo2.toString());
Which is not valid in this case:
Date date1 = new Date(date2.toString());
You will need to write some conversion code and reference it from an external bindings file:
<jxb:bindings xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb" version="2.1">
<jxb:bindings schemaLocation="format.xsd">
<jxb:bindings node="//xs:element[#name='my-date']">
<jxb:property>
<jxb:baseType>
<jxb:javaType name="java.util.Date"
parseMethod="org.example.DateFormatter.parseInt"
printMethod="org.example.DateFormatter.printInt" />
</jxb:baseType>
</jxb:property>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
For More Information
http://blog.bdoughan.com/2011/08/xml-schema-to-java-generating.html

Webservice Namespace problem

I have a ASP.NET webservice which is called by a Java client. The client sends a SOAP message as input but the problem is that it never reaches my webservice method. I always get null input. I used TraceListener and saw this warning which may cause the problem:
The element was not expected in this context: ... Expected elements: http://client.ns.url/:ListenerInput.
This is what the client sends:
<?xml version="1.0" encoding="utf-8"?>
<S:Envelope xmlns:S = "http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:Listener xmlns:ns2 = "http://client.ns.url/">
<ListenerInput>
<errorCode>0</errorCode>
<errorDescription>Success</errorDescription>
<firstPrice xsi:nil = "true" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"/>
</ListenerInput>
</ns2:Listener>
</S:Body>
</S:Envelope>
And here's my webmethod:
[System.Web.Services.WebServiceAttribute(Namespace = "http://client.ns.url/")]
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")]
[System.Web.Services.WebServiceBindingAttribute(Name = "ListenerWebServicePortSoapBinding", Namespace = "http://client.ns.url/")]
public partial class ListenerService : System.Web.Services.WebService
{
[System.Web.Services.WebMethodAttribute()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://client.different.url/services/action/ListenerService/Listener", RequestElementName = "Listener", RequestNamespace = "http://client.ns.url/", ResponseNamespace = "http://client.ns.url/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("return")]
public ListenerResponse Listener([System.Xml.Serialization.XmlElementAttribute("ListenerInput")]ListenerInput listenerInput)
{
..
This is the input class:
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://client.ns.url")]
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
public partial class ListenerInput
{
public int errorCode;
public string errorDescription;
public float? firstPrice;
}
What should I do to solve this? Here's the some of the trace log:
Calling IHttpHandler.ProcessRequest
Caller: System.Web.Services.Protocols.SyncSessionlessHandler#47754503::ProcessRequest()
Request Host Address: xxx
Request Url: [POST] http:/my.website/Listener.asmx
..
Calling XmlSerializer [Read Request]
Method: Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer#53218107::Deserialize(System.Web.Services.Protocols.SoapServerProtocol+SoapEnvelopeReader#4153573=.., (null))
Caller: System.Web.Services.Protocols.SoapServerProtocol#51389704::ReadParameters()
...
The element was not expected in this context: <ListenerInput>..</ListenerInput>. Expected elements: http://client.ns.url/:ListenerInput.
...
Return from XmlSerializer [Read Request]
Caller: System.Web.Services.Protocols.SoapServerProtocol#51389704::ReadParameters()
...
Calling ListenerResponse Listener(ListenerInput)
Method: ListenerService#64693151::Listener((null))
Caller: System.Web.Services.Protocols.SyncSessionlessHandler#47754503::Invoke()
Solved the problem. I was using client's namespace address when I should be using my own. Changed this:
[System.Web.Services.WebServiceBindingAttribute(Name = "ListenerWebServicePortSoapBinding", Namespace = "http://client.ns.url/")]
public partial class ListenerService : System.Web.Services.WebService
to this:
[System.Web.Services.WebServiceBindingAttribute(Name = "ListenerWebServicePortSoapBinding", Namespace = "http://my.ns.url/")]
public partial class ListenerService : System.Web.Services.WebService