SpringBatch Jaxb2Marshaller: different name of class and xml attribute - java

I try to read an xml file as input for spring batch:
Java Class:
package de.example.schema.processes.standardprocess;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Process", namespace = "http://schema.example.de/processes/process", propOrder = {
"input"
})
public class Process implements Serializable
{
#XmlElement(namespace = "http://schema.example.de/processes/process")
protected ProcessInput input;
public ProcessInput getInput() {
return input;
}
public void setInput(ProcessInput value) {
this.input = value;
}
}
SpringBatch dev-job.xml:
<bean id="exampleReader" class="org.springframework.batch.item.xml.StaxEventItemReader" scope="step">
<property name="fragmentRootElementName" value="input" />
<property name="resource"
value="file:#{jobParameters['dateiname']}" />
<property name="unmarshaller" ref="jaxb2Marshaller" />
</bean>
<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>de.example.schema.processes.standardprocess.Process</value>
<value>de.example.schema.processes.standardprocess.ProcessInput</value>
...
</list>
</property>
</bean>
Input file:
<?xml version="1.0" encoding="UTF-8"?>
<process:process xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:process="http://schema.example.de/processes/process">
<process:input>
...
</process:input>
</process:process>
It fires the following exception:
[javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schema.example.de/processes/process", local:"input"). Expected elements are <<{http://schema.example.de/processes/process}processInput>]
at org.springframework.oxm.jaxb.JaxbUtils.convertJaxbException(JaxbUtils.java:92)
at org.springframework.oxm.jaxb.AbstractJaxbMarshaller.convertJaxbException(AbstractJaxbMarshaller.java:143)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:428)
If I change to in xml it work's fine. Unfortunately I can change neither the xml nor the java class.
Is there a possibility to make Jaxb2Marshaller map the element 'input' to the class 'ProcessInput'?

I don't believe JAXB allows this. JAXB is a binding API, so it doesn't provide much in the way of customization. That being said, you can use XStream and provide aliases for what you need, allowing you to customize the mapping of XML to object however you want. You can see an XStream example here: https://github.com/spring-projects/spring-batch/blob/master/spring-batch-samples/src/main/resources/jobs/iosample/xml.xml

Related

Spring MongoDB inserting unwanted objects

I haven't been able to find a single web page or other post about this issue. Hence, I'm here posting.
Within the documents I am storing to my mongodb, I have these things showing up:
"itemModifiers" : [
{
"val$implicitModifierString" : "16% increased Spell Damage",
"modifierName" : "16% increased Spell Damage"
}
]
The val$implicitModifierString is actually a variable from within my Java code, which was not set to the ItemModifiers.class instance. Basically, when I set a variable in my classes which I am storing to MongoDb, any variable or Object that I use to set that variable is also getting stored to the database (or at least that is what it looks like to me!).
Here is some sample code of what the process looks like (if you hate maps, sorry; not really relevant here.):
public ItemModifier deriveModifier(final String modifier) {
for (Pattern outerKey : tierMap.keySet()) {
if (outerKey.matcher(modifier).matches()) {
HashMap<Pattern, ItemModifierTier> innerMap = tierMap.get(outerKey);
for (Pattern innerKey : innerMap.keySet()) {
if (innerKey.matcher(modifier).matches()) {
Matcher innerMatcher = innerKey.matcher(modifier);
Double[] tierValues = new Double[innerMatcher.groupCount()];
innerMatcher.find();
for (int i = 1; i <= innerMatcher.groupCount(); i++) {
tierValues[i - 1] = Double.valueOf(innerMatcher.group(i));
}
return new ItemModifier() {{
setModifierName(modifier);
setModifierTerm(termMap.get(outerKey.pattern()));
setModifierTier(innerMap.get(innerKey));
setModifierType(itemModifierType);
setModifierValues(tierValues);
}};
}
}
}
}
return null;
}
And here is the ItemModifier class (intentionally indexed every field because they are all queryable via a service; I have not yet created composite indexes but plan to once the issue at hand is sorted):
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"modifierName",
"modifierTerm",
"modifierType",
"modifierTier",
"modifierValues",
"modifierAverage"
})
public class ItemModifier {
#Indexed
#JsonProperty("modifierName")
private String modifierName;
#Indexed
#JsonProperty("modifierTerm")
private String modifierTerm;
#Indexed
#JsonProperty("modifierType")
private ItemModifierType modifierType;
#Indexed
#JsonProperty("modifierTier")
private ItemModifierTier modifierTier;
#Indexed
#JsonProperty("modifierValues")
private Double[] modifierValues;
#Indexed
#JsonProperty("modifierAverage")
private Double modifierAverage;
public ItemModifier() {
}
public String getModifierName() {
return modifierName;
}
public void setModifierName(String modifierName) {
this.modifierName = modifierName;
}
//... the other getters/setters
}
This ItemModifiers.class is held within an ItemDocument.class and is stored to the mongo database simply by invoking mongoOperations.insert(itemDocumentInstance);.
In case it matters, this is my mongoConfig.xml:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<mongo:mongo host="127.0.0.1" port="27017"/>
<mongo:db-factory dbname="public-stash-api"/>
<bean id="mappingContext"
class="org.springframework.data.mongodb.core.mapping.MongoMappingContext"/>
<bean id="defaultMongoTypeMapper"
class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
<constructor-arg name="typeKey">
<null/>
</constructor-arg>
</bean>
<bean id="mappingMongoConverter"
class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mappingContext" ref="mappingContext"/>
<property name="typeMapper" ref="defaultMongoTypeMapper"/>
</bean>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mongoConverter" ref="mappingMongoConverter"/>
</bean>
</beans>
Thank you in advance for your help!
You are mixing Jackson with MongoDB. The fact that MongoDB uses documents does not mean it is using Jackson. MongoDB stores document in BSON (Binary JSON) format but you can't customize the way your documents are stored by using Jackson annotations.
There are Spring Data MongoDB annotations (like org.springframework.data.mongodb.core.mapping.Field) for that very purpose. You actually used one of them in your code (org.springframework.data.mongodb.core.index.Indexed).
As it turns out, this is the culprit:
return new ItemModifier() {{
setModifierName(modifier);
setModifierTerm(termMap.get(outerKey.pattern()));
setModifierTier(innerMap.get(innerKey));
setModifierType(itemModifierType);
setModifierValues(tierValues);
}};
The static instantiation is what caused the weird val$inputVariables to persist into the Mongo Documents via Spring.

How can I execute a stored procedure with JPA & Spring Data?

I am trying to call the Terminal_GetTicket stored procedure in my database but keep getting the following exception:
PropertyReferenceException: No property getTicket found for type TicketInfo
I have cross validated my configuration with a very simple test entity and everything seems to work fine, however for the actual case, something is wrong.
Here is my domain entity (TicketInfo):
#Entity
#NamedStoredProcedureQuery(name = "TicketInfo.getTicket", procedureName = "Terminal_GetTicket", resultClasses = TicketInfo.class, parameters = {
#StoredProcedureParameter(mode = ParameterMode.IN, name = "sys_id_game", type = Integer.class)})
public class TicketInfo {
#Id #GeneratedValue
private Long id;
private String idTicket;
private Integer externalTicketCode;
private Short sequenseAlert;
private Integer dlTimeStamp;
All the instance variables have their getters and setters properly defined and the stored procedure has a total of 5 output parameters matching the attributes of TicketInfo.
Furthermore, here is my repository interface:
public interface TicketInfoRepository extends CrudRepository<TicketInfo, Long> {
#Transactional(timeout = 5)
#Procedure
TicketInfo getTicket(Integer sys_id_game);
}
Also, here is my context.xml file (for Spring):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:repository="http://www.springframework.org/schema/data/repository"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd
http://www.springframework.org/schema/data/repository
http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd">
<context:component-scan base-package="ar.com.boldt.godzilla" />
<jpa:repositories base-package="xx.xxx.xxx.godzilla.business.dao" />
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="${dataSource.show.sql}" />
<property name="generateDdl" value="false" />
<property name="database" value="SQL_SERVER" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<!-- spring based scanning for entity classes -->
<property name="packagesToScan" value="xx.xxx.xxx.godzilla.business.dao" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" />
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcache" />
</bean>
<bean id="ehcache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
</bean>
</beans>
And finally a watered-down version of the stored procedure itself:
ALTER PROCEDURE [Terminal_GetTicket](
#arg int
,#res int output
,#res2 int output
)
as
Declare #error int
select 0, 1, 2
RETURN #error
Now, whenever I try setting the #Autowired annotation, I get the exception mentioned above.
I remember that I have been struggling with the MS SQL stored procedures and spring-data-jpa. This is how I have been able to successfully run it:
Model:
#NamedNativeQueries({
#NamedNativeQuery(
name = "yourInternalName",
query = "EXEC [procedure_name] :param1, :param2",
resultClass = Foo.class
)
})
#Entity
public class Foo{
/* Fields, getters, setters*/
}
That's pretty straightforward. This approach is different though, you are not declaring procedures directly (that's also the reason why it doesn't have to work if you decide to change RDBS).
Then you have to extend your repository:
public interface FooRepositoryCustom {
Foo fancyMethodName(arg1, arg2);
}
And directly implement it:
public class FooRepositoryImpl implements FooRepositoryCustom {
#PersistenceContext
EntityManager entityManager;
#Override
public Foo fancyMethodName(arg1, arg2) {
Query query = entityManager.createNamedQuery("yourInternalName");
query.setParameter("param1", arg1);
query.setParameter("param2", arg2);
return query.getResultList();
}
Let's put it all together:
public interface FooRepository extends CrudRepository<Foo, Long>, FooRepositoryCustom {
}
Note that if you decide to return for example a List of Foo objects you only edit return value in your custom repository.
I followed SirKometas advice but I could not get it to work so I came up with something that worked for me and I think from syntax point of view is better. First create your entity class like below.
#NamedStoredProcedureQueries({//
#NamedStoredProcedureQuery(//
name = "MySP"//
, procedureName = "my_sp"//
, parameters = { //
#StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = String.class)}//
, resultClasses = Foo.class)//})
#Entity
public class Foo {
Then the Implementation class of the repository would be:
#Component
public class FooRepositoryImpl implements FooCustomRepository {
#PersistenceContext
EntityManager entityManager;
#Override
public List<Foo> foo(String arg) {
Query query = entityManager.createNamedStoredProcedureQuery("MySP");
query.setParameter("arg", arg);
return query.getResultList();
}
}
The rest of the implementation is like the answer from SirKometa above. Think also that you have to create a EntityManager bean in your application for this to work.

Using Spring/ Hibernate /Jackson. Getting error -- Could not read JSON: Could not initialize class exception

We are using the combination of Jackson, spring and hibernate in our application.
Below code I am listing is working fine
Controller
#RequestMapping(value = ServiceEndpoints.MyService, method = RequestMethod.POST)
#ResponseBody
public MyResponse updateEntity(final HttpServletRequest request, #RequestBody final MyEntity myEntity ) {
//Service Call
}
Entity
#Entity
public class MyEntity extends BaseEntity implements Serializable {
#Column
private String metaTags;
public String getMetaTags() {
return metaTags;
}
public void setMetaTags(String metaTags) {
this.metaTags = metaTags;
}
servlet-contect.xml
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<ref bean="jacksonObjectMapper" />
</property>
</bean>
</list>
</property>
</bean>
The above code works Fine. We get data from UI, it gets converted from JSON to entity and we are able to save the same.
But then i added a code to clean some charatcers from the Entity field as shown below
public void setMetaTags(String comments) {
this.metaTags = MyClassWithStaticMethods.OnesuchStaticMethod(metaTags);
}
I get the below exception
Could not read JSON: Could not initialize class MyClassWithStaticMethods (through reference chain: MyEntity["metaTags"]);
nested exception is org.codehaus.jackson.map.JsonMappingException: Could not initialize class MyClassWithStaticMethods (through reference chain: MyEntity["metaTags"])
Now i did go through some links like this one
http://cowtowncoder.com/blog/archives/2010/08/entry_411.html
but this link is not really related to my problem.
Can any one please help
The issue was multiple versions of the same jar which was in turn used by "MyClassWithStaticMethods" (Thanks #Abhijith Nagarajan for asking to look into it).
My method was using guava API and another part of the application was using the old google-collections. These two were creating the problem.
Regards

ConversionNotSupportedException when using RuntimeBeanRefrence for a list of objects

We are currently using spring framework and are using following XML :-
<bean id="A" class="com.foo.baar.A" >
<property name="attributes">
<set value-type="com.foo.bar.B">
<ref bean="X" />
<ref bean="Y" />
</set>
</property>
</bean>
<bean id="X" class="com.foo.bar.X" />
<bean id="Y" class="com.foo.bar.Y" />
where class X and class Y extend class B
class A has setter as follows :-
public void setAttributes(List<B> attributes) {
this.attributes = attributes;
}
Now, I have to eliminate the above XML and I am setting the beans programatically as following :-
List<Object> beanRefrences = new ArrayList<Object>();
for(String attribute : attributes) {
Object beanReference = new RuntimeBeanReference(attribute);
beanRefrences.add(beanReference);
}
mutablePropertyValues.add(propertyName, beanRefrences);
With above code, I am getting following error :-
nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.util.ArrayList' to required type 'java.util.List' for property 'attributes';
nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.springframework.beans.factory.config.RuntimeBeanReference] to required type [com.foo.bar.B] for property 'attributes[0]': no matching editors or conversion strategy found
Can anyone give me pointers on how to make it work correctly?
After taking a look at Spring's implementation of BeanDefinitionValueResolver one can see that a traditional, ordinary List is not sufficient. You need to use a ManagedList:
List<Object> beanRefrences = new ManagedList<>();
for(String attribute : attributes) {
Object beanReference = new RuntimeBeanReference(attribute);
beanRefrences.add(beanReference);
}
mutablePropertyValues.add(propertyName, beanRefrences);

No endpoint mapping found for..., using SpringWS, JaxB Marshaller

I get this error: No endpoint mapping found for [SaajSoapMessage {http://mycompany/coolservice/specs}ChangePerson]
Following is my ws config file:
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<description>An endpoint mapping strategy that looks for #Endpoint and #PayloadRoot annotations.</description>
</bean>
<bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
<description>Enables the MessageDispatchServlet to invoke methods requiring OXM marshalling.</description>
<constructor-arg ref="marshaller"/>
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPaths">
<list>
<value>org.company.xml.persons</value>
<value>org.company.xml.person_allextensions</value>
<value>generated</value>
</list>
</property>
</bean>
<bean id="persons" class="com.easy95.springws.wsdl.wsdl11.MultiPrefixWSDL11Definition">
<property name="schemaCollection" ref="schemaCollection"/>
<property name="portTypeName" value="persons"/>
<property name="locationUri" value="/ws/personnelService/"/>
<property name="targetNamespace" value="http://mycompany/coolservice/specs/definitions"/>
</bean>
<bean id="schemaCollection" class="org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection">
<property name="xsds">
<list>
<value>/DataContract/Person-AllExtensions.xsd</value>
<value>/DataContract/Person.xsd</value>
</list>
</property>
<property name="inline" value="true"/>
</bean>
I have then the following files:
public interface MarshallingPersonService {
public final static String NAMESPACE = "http://mycompany/coolservice/specs";
public final static String CHANGE_PERSON = "ChangePerson";
public RespondPersonType changePerson(ChangePersonType request);
}
and
#Endpoint
public class PersonEndPoint implements MarshallingPersonService {
#PayloadRoot(localPart=CHANGE_PERSON, namespace=NAMESPACE)
public RespondPersonType changePerson(ChangePersonType request) {
System.out.println("Received a request, is request null? " + (request == null ? "yes" : "no"));
return null;
}
}
I am pretty much new to WebServices, and not very comfortable with annotations. I am following a tutorial on setting up jaxb marshaller in springws. I would rather use xml mappings than annotations, although for now I am getting the error message.
EDIT: ChangePersonType
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ChangePersonType", propOrder = {
"applicationArea",
"dataArea"
})
public class ChangePersonType {
#XmlElement(name = "ApplicationArea", namespace = "http://mycompany/coolservice/specs", required = true)
protected TransApplicationAreaType applicationArea;
#XmlElement(name = "DataArea", namespace = "http://mycompany/coolservice/specs", required = true)
protected DataArea dataArea;
#XmlAttribute(required = true)
#XmlJavaTypeAdapter(NormalizedStringAdapter.class)
protected String releaseID;
#XmlAttribute
#XmlJavaTypeAdapter(NormalizedStringAdapter.class)
protected String versionID;
--The rest are getters and setters.
I solved it. The parameter of the end point class and return variable had to be wrapped in JAXBElement, like JAXBElement.
The reason is
The classes generated by JAXB2 from
your schema come in two flavors: those
that have a #XmlRootElement
annotation, which can be used directly
as either parameter or response, and
those who haven't. Those classes which
haven't got this annotation need to be
wrapped in a JAXBElement.
Besides the generated classes from
your schema, JAXB2 also generates an
ObjectFactory class, which clarifies
the use of JAXBElement. There are some
factory methods is there, which
illustrate how you can use the various
schema types.
Arjen Poutsma
h ttp://forum.springsource.org/showthread.php?t=49817

Categories

Resources