Spring OXM 3 + JiBXException: No marshaller defined for class - spring-ws

I have used Spring OXM and JiBX in my application.
below is my Spring Config file
<context:component-scan base-package="com.controller"/>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<oxm:jibx-marshaller target-class="com.request.RequestClass" id="rqMarshaller"/>
<oxm:jibx-marshaller target-class="com.response.ResponseClass" id="rsMarshaller"/>
<bean id="xmlViewer" class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg ref="rsMarshaller" />
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
below is controller class
#Controller
public class MyController {
#Autowired
private JibxMarshaller rqMarshaller;
#RequestMapping(value = "/myrequest", method = RequestMethod.POST)
public ModelAndView searchFlights(#RequestBody String request) {
System.out.println("Inside");
System.out.println("request = "+request);
Source source = new StreamSource(new StringReader(request));
RequestClass rq = null;
try {
rq = (RequestClass) rqMarshaller.unmarshal(source);
} catch (XmlMappingException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
ResponseClass e = new ResponseClass();
e.setVersion("2.0");
Orig ond = new Orig();
ond.setCode("AIT");
e.getOrig().add(ond);
return new ModelAndView("xmlViewer","object",e);
}
}
When i send XML request it marshaled successfully but for response i got following error message.
org.jibx.runtime.JiBXException: No marshaller defined for class com.response.ResponseClass
I have already defined marshaller for ResponseClassin spring config file.
please help. Thanks.

Finally i figured out the solution !!!
Need to specify the bindingName attribute while registering the JiBxMarshaller.
<oxm:jibx-marshaller target-class="com.request.RequestClass" id="rqMarshaller" bindingName="rqBinding"/>
<oxm:jibx-marshaller target-class="com.response.ResponseClass" id="rsMarshaller" bindingName="rsBinding/>
and specify same name in respective binding/mapping file of JiBX.
That's it !

Related

Custom SAMLUserDetailsService not populating custom UserDetails

I have a Spring project and I'm converting my current authentication to use SAML2.
I have everything working as far as authentication, but I'm having difficulty in getting the SAML2 extension to insert my custom UserDetails object into the Spring Security Context authentication object.
I have a custom UserDetailsService, defined below:
public class SAMLAuthManager implements SAMLUserDetailsService {
private static final Logger logger = Logger.getLogger(JDBCAuthManager.class);
#Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
logger.info("Credential attributes: " + credential.getAttributes());
for (int x = 0; x < credential.getAttributes().size(); x++) {
Attribute attr = credential.getAttributes().get(x);
List<XMLObject> attrValues = attr.getAttributeValues();
StringBuilder strBuilder = new StringBuilder();
for (int g = 0; g < attrValues.size(); g++) {
XMLObject currObj = attrValues.get(g);
strBuilder.append(currObj.toString()).append(",");
}
strBuilder.deleteCharAt(strBuilder.length() - 1);
logger.info(attr.getFriendlyName() + ", " + strBuilder.toString());
}
String username = credential.getNameID().getValue();
userWrapper.setStaff(s);
logger.info("Returning wrapper: " + userWrapper);
return userWrapper;
} else {
return null;
}
}
}
I have also configured this userDetails in my security context config:
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<property name="userDetails" ref="samlUserDetails" />
</bean>
However, when I inspect the SecurityContextHolder, post authentication, this line:
SecurityContextHolder.getContext().getAuthentication().getCredentials();
returns an object of type org.springframework.security.saml.SAMLCredential.
I checked to see if Spring populated the Principal with the custom object (SecurityContextHolder.getContext().getAuthentication().getPrincipal()) but it did not, that's just a String with the username populated.
Any ideas?
Thanks
The principal is by default forced to be String (in order to always permit replication of Principal which was earlier an un-serializable NameID).
This can be changed by setting forcePrincipalAsString in SAMLAuthenticationProvider to false, which will make Spring SAML include your object provided by SAMLUserDetailsService as principal in the Authentication object.
The result of call to SAMLUserDetailsService is always available under SecurityContextHolder.getContext().getAuthentication().getDetails().

Authentication of SOAP messages

I have searched for a good example and cannopt find one. I want to take the username and password from the SOAP header, and set the spring security context after I authenticate using our exisiting service methods. I have implemented the Wss4jSecurityInterceptor and it validates the header element. WHat I need to do in the callback, or some other mechanism, is create an uthetication context so I can access it later in our endpoint.
However, I dont think that the callback is the correct place to do it, as I keep getting password supplied no password errors. I am new to spring security and integration.
Config:
<bean id="SOAPSecurityInterceptor" class="com.ps.snt.ws.interceptor.SOAPSecurityInterceptor">
<property name="validationActions" value="UsernameToken"/>
<property name="validationCallbackHandler" ref="callbackHandler"/>
</bean>
<bean id="callbackHandler" class="com.ps.snt.ws.interceptor.SOAPSecurityValidationCallbackHandler">
</bean>
callback:
public class SOAPSecurityValidationCallbackHandler extends SimplePasswordValidationCallbackHandler {
#Override
protected void handleUsernameToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
System.out.println("In security callback " + callback.getPassword());
boolean valid = true;
String token = callback.getIdentifier();
String password = callback.getPassword();
Integer zoneID = null;
String username = null;
StringBuffer errorMessages = new StringBuffer();
if(StringUtils.isEmpty(token)) {
errorMessages.append("Username token cannot be empty");
valid = false;
} else {
Pattern pattern = Pattern.compile("^[\\w]+\\d\\d\\d\\d\\d");
Matcher matcher = pattern.matcher(token);
if(!matcher.matches()) {
valid = false;
errorMessages.append("Username token must be in the format 'user#zone'.");
}
else {
String[] parts = token.split("#");
username = parts[0];
zoneID = Integer.parseInt(parts[1]);
}
}
if(StringUtils.isEmpty(password)) {
errorMessages.append("Password cannot be empty.");
valid = false;
}
if(valid && username != null && zoneID != null) {
LoginService loginService = new LoginService();
LoginContextDO loginContextDO = loginService.getAuthenticatedLoginContext(username, password, zoneID);
AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password);
authentication.setDetails(loginContextDO);
authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
System.out.println("Authetnication failed!");
}
}
}
My requirements are simple:
- validate the SOAP header (works)
- retrieve the username and password
- call our legacy service to create our login context
- set the spring security context (with logincontext as details) so I can use later in an endpoint
What mechanism can I use to validate the soap header and set a security context from that header?
SpringSecurityPasswordValidationCallbackHandler is for you. From Spring WS docs:
The SpringSecurityPasswordValidationCallbackHandler validates plain text and digest passwords using a Spring Security UserDetailService to operate. It uses this service to retrieve the (digest of ) the password of the user specified in the token. The (digest of) the password contained in this details object is then compared with the digest in the message. If they are equal, the user has successfully authenticated, and a UsernamePasswordAuthenticationToken is stored in theSecurityContextHolder. You can set the service using the userDetailsService. Additionally, you can set a userCache property, to cache loaded user details.
<beans>
<bean class="org.springframework.ws.soap.security.wss4j.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>

Accessing flow state name from filter

I'm working on a large Grails 1.3.7 project and I want to access the flow state name from a filter for logging purposes. I've been googling a lot and the closest answer I could find was: Grails WebFlow State Name, but it only works from within the flow itself.
Is there any way to obtain the state name of the flow that is being executed in current session from outside the flow (the filter)?
Thanks in advance,
Guillermo
After a lot of googling and debugging I managed to produce the following code. It works in a simple application. I'll integrate it with the main application when I come back from holidays and then I'll update this question.
package org.glalejos
import org.springframework.context.ApplicationContext
import org.springframework.webflow.context.ExternalContext
import org.springframework.webflow.context.ExternalContextHolder
import org.springframework.webflow.context.servlet.ServletExternalContext
import org.springframework.webflow.execution.FlowExecution
import org.springframework.webflow.execution.FlowExecutionKey
import org.springframework.webflow.execution.repository.FlowExecutionRepository
class LoggingFilters {
def grailsApplication
String getFlowStateName(def grailsApplication, def servletContext, def request, def response) {
String stateName
if (grailsApplication && servletContext && request && request.queryString && response) {
try {
String strKey = null
String[] keys = request.queryString.split("&")
keys.each{ if (it.startsWith("execution=")) strKey = it.substring(10)}
if (strKey != null) {
ApplicationContext ctx = grailsApplication.mainContext
FlowExecutionRepository fer = ctx.getBean("flowExecutionRepository")
FlowExecutionKey fek = fer.parseFlowExecutionKey(strKey)
ExternalContext previousContext = ExternalContextHolder.getExternalContext()
try {
// You have to set an external context before invoking "fer.getFlowExecution()" or it'll throw a NPE
ExternalContextHolder.setExternalContext(new ServletExternalContext(servletContext, request, response));
FlowExecution fe = fer.getFlowExecution(fek)
stateName = fe.getActiveSession().getState().getId()
} finally {
ExternalContextHolder.setExternalContext(previousContext);
}
} else {
stateName = null
}
} catch(Exception e) {
stateName = null
}
} else {
stateName = null
}
return stateName
}
def filters = {
logData(controller:"*", action:"*") {
before = {
println("Incoming request. Current flow state name is: ${getFlowStateName(grailsApplication, servletContext, request, response)}")
}
after = {
println("Dispatched request. Current flow state name is: ${getFlowStateName(grailsApplication, servletContext, request, response)}")
}
}
}
}
EDIT: The code above is OK to determine the name of the current flow state at a given point in time, but it won't update the Mapped Diagnostic Context of the logging framework as the flow execution evolves. For this purpose it is necessary to implement a org.springframework.webflow.execution.FlowExecutionListener and register it in conf/spring/resources.groovy:
beans = {
myLoggingFlowExecutionListener(org.example.MyLoggingFlowExecutionListener)
}
You have to register this listener bean and the hibernateConversationListener bean in a executionListenerLoader bean, but, for some reason, Spring DSL doesn't work in this case (see EDIT2 below). So here is the resources.xml you can place in the same folder as resources.groovy in order to properly declare your resources:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="executionListenerLoader" class="org.springframework.webflow.execution.factory.StaticFlowExecutionListenerLoader">
<constructor-arg>
<list>
<ref bean="hibernateConversationListener" />
<ref bean="myLoggingFlowExecutionListener" />
</list>
</constructor-arg>
</bean>
</beans>
Each FlowExecutionListener method receives a lot of context information that can be used for logging purposes (I'm ommiting the implementation of this class for clarity).
EDIT2: Failing to add the hibernateConversationListener bean to the executionListenerLoader results in Hibernate exceptions when manipulating domain objects during the lifecycle of the flow. However, Spring DSL doesn't work in this specific case, so I had to declare the required beans using XML format. See http://grails.1312388.n4.nabble.com/Registering-custom-flow-execution-listener-td2279764.html. I've updated the code above to the final, working, version.

Custom validation error messages using Jaxb2marshaller and spring-ws

I have a running spring-ws project that can unmarshal requests using Jax2b, but when unmarshalling of integers/booleans fail I get an error message with little detail and often without the name of the invalid element. E.g.:
org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; cvc-datatype-valid.1.2.1: '' is not a valid value for 'integer'.]
This also becomes the content of the SOAPFault response from my webservice.
I am trying to change the message to include the element name. I'm using a ValidationEventHandler to change the message by throwing a RuntimeException from the event handler, but it only works i some cases.
ValidationEventHandler:
#Component
public class ValidationEventHandlerImpl implements ValidationEventHandler {
#Override
public boolean handleEvent(ValidationEvent event) {
String message = event.getMessage();
String linkedMessage = "";
if(event.getLinkedException() != null)
linkedMessage = event.getLinkedException().toString();
boolean ignoreValidationEvent = true;
if(message.contains("NumberFormatException") ||
message.contains("is not a valid value") ||
linkedMessage.contains("NumberFormatException") ||
linkedMessage.contains("is not a valid value")){
ignoreValidationEvent = false;
}
if(ignoreValidationEvent){
return true;
}else{
String nodeName = "";
if(event.getLocator() != null && event.getLocator().getNode() != null)
nodeName = event.getLocator().getNode().getNodeName();
//This is the important line
throw new RuntimeException("Error parsing '" + nodeName + "': " + event.getMessage());
}
}
}
It successfully changes:
JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException: Not a number: 32g321
- with linked exception:
[java.lang.NumberFormatException: Not a number: 32g321]
to: RuntimeException message: "java.lang.RuntimeException: Error parsing 'MyNodeName': Not a number: 32g321"
(Event Severity: ERROR)
But it does not work when I want it to change:
JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; cvc-datatype-valid.1.2.1: '' is not a valid value for 'integer'.]
to: RuntimeException message: "java.lang.RuntimeException: Error parsing 'MyNodeName': '' is not a valid value for 'integer'".
The RuntimeException is ignored and the SAXParseException is thrown instead and added to the SOAPFault reponse.
(Event Severity: FATAL_ERROR)
Spring configuration for Jaxb2Marshalling:
<bean id="jaxb2MarshallerContact" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.model.oxm.ContactRequest</value>
<value>com.model.oxm.ContactResponse</value>
</list>
</property>
<property name="marshallerProperties">
<map>
<entry>
<key>
<util:constant static-field="javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT" />
</key>
<value type="boolean">true</value>
</entry>
<entry>
<key>
<util:constant static-field="javax.xml.bind.Marshaller.JAXB_FRAGMENT" />
</key>
<value type="boolean">true</value>
</entry>
</map>
</property>
<property name="schema" ref="ContactServiceSchema" />
<property name="validationEventHandler" ref="validationEventHandlerImpl" />
</bean>
<bean id="ContactServiceSchema" class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="WEB-INF/schemas/ContactService.xsd" />
</bean>
Endpoint:
#Endpoint
public class ContactEndpoint {
private Logger logger = Logger.getLogger(ContactEndpoint.class);
#Autowired
private ContactService contactService;
private static final String NAMESPACE_URI = "http://mydomain/schemas";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "ContactRequest")
#ResponsePayload
public ContactResponse handleContactRequest(#RequestPayload ContactRequest contactRequest) throws Exception {
...
How can I return a custom message instead of the SAXParseException message?
Is there a better way of implementing this, e.g. using ValidationErrorHandler?
Thanks!
I finally found a way around this issue. Instead of throwing a new RuntimeException from the ValidationEventHandler, I added it as a suppressed exception on the events linked exception:
event.getLinkedException().addSuppressed(new RuntimeException(errorMessage));
and in the Endpoint I changed the RequestPayload the soapenvelope instead. The marshallingService wraps the jax2bmarshaller:
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "ContactRequest")
#ResponsePayload
public ContactResponse handleContactRequest(#RequestPayload SoapEnvelope soapEnvelope) throws Exception {
ContactRequest contactRequest = marshallingService.unmarshalContact(soapEnvelope.getBody().getPayloadSource());
the marshallingService catch the exception, extract my suppressed exception message and throws that instead:
((UnmarshalException) xmlMappingException.getCause()).getLinkedException().getSuppressed()[0].getLocalizedMessage();
It is not an elegant solution but the endpoint produces much better error messages than before.
Have you tried using the PayloadValidatingInterceptor? That will validate the message before demarshalling as far as I know. Perhaps that'll give you some more insight in what is going wrong.
You can simply set it up in your application config:
private ClassPathResource yourXsdFile = new ClassPathResource("xsd.xsd");
#Bean
public PayloadValidatingInterceptor validatingInterceptor() {
PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
interceptor.setSchema(yourXsdFile);
interceptor.setAddValidationErrorDetail(true);
return interceptor;
}
#Bean
public PayloadRootAnnotationMethodEndpointMapping endpointMapping() {
PayloadRootAnnotationMethodEndpointMapping mapping = new PayloadRootAnnotationMethodEndpointMapping();
mapping.setInterceptors(new EndpointInterceptor[]{
validatingInterceptor()
});
return mapping;
}
For more info check out the api docs.

JAX-WS & JSR 330 (Spring) - cannot inject dependency

I cannot seem to inject a simple bean into my #WebService. With Spring on the classpath and javax.inject dependencies defined, I created a simple JAX-WS webservice with some underlying interface-driven DAOs etc:
#Named
#WebService(name = "NoteStorage", serviceName = "NoteStorageWS")
public class NoteStorageWS implements NoteStore {
private static final Log l = LogFactory.getLog(NoteStorageWS.class);
#Named("NoteDAO")
#Inject
private NoteDAO noteDAO;
public NoteStorageWS() {
super();
}
#Override
#WebMethod
public StorageState takeNote(String note) {
try {
l.info(format("Service received message: '%s'", note));
Note n = new Note();
n.setContent(note);
noteDAO.store(n);
} catch (Exception e) {
l.error(e);
return StorageState.FAILURE;
}
return StorageState.SUCCESS;
}
#WebMethod(exclude = true)
public void setNoteDAO(NoteDAO noteDAO) {
this.noteDAO = noteDAO;
}
}
NoteDAOhas just implementation: FlatFileNoteDAO which is defined as follows:
#Named("NoteDAO")
public class FlatFileNoteDAO implements NoteDAO {
private static final Log l = LogFactory.getLog(FlatFileNoteDAO.class);
#Override
public void store(Note n) {
if (n == null) {
throw new IllegalArgumentException("Note was null");
}
try {
l.info(format("Storing note '%s'", n));
FileWriter fileWriter = new FileWriter(new File("Note"));
fileWriter.write(format("%s\n", n.getContent()));
fileWriter.close();
} catch (IOException e) {
throw new DataAccessException(e);
}
}
}
My web.xml says:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<resource-env-ref>
<description>Object factory for the CDI Bean Manager</description>
<resource-env-ref-name>BeanManager</resource-env-ref-name>
<resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
</resource-env-ref>
</web-app>
I deploy the application to Glassfish by pointing it to the target/note-ws/ directory and execute the simple takeNote method via the ?Tester page.
Upon submission of the tester form I get a NullPointerException at the line noteDAO.store(n);, presumably because noteDAO wasn't injected.
I can confirm that Spring has been invoked by the logs from glassfish on context initialisation (the Java EE context):
[#|2011-12-04T16:57:24.970+0000|INFO|glassfish3.1.1|org.springframework.context.annotation.ClassPathBeanDefinitionScanner|_ThreadID=256;_ThreadName=Thread-2;|JSR-330 'javax.inject.Named' annotation found and supported for component scanning|#]
[#|2011-12-04T16:57:25.653+0000|INFO|glassfish3.1.1|org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor|_ThreadID=256;_ThreadName=Thread-2;|JSR-330 'javax.inject.Inject' annotation found and supported for autowiring|#]
[#|2011-12-04T16:57:25.757+0000|INFO|glassfish3.1.1|org.springframework.beans.factory.support.DefaultListableBeanFactory|_ThreadID=256;_ThreadName=Thread-2;|Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#9e39146: defining beans [noteStorageWS,NoteDAO,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor]; root of factory hierarchy|#]
which says my beans are defined: noteStorageWS, NoteDAO and so-on.
Any ideas?
Edit
to clarify, I'm using Spring to provide JSR 330 -- dependency injection -- functionality.
I never got this solved, so I ended up removing the DI-features as the codebase was small and relied on manual dependency resolution - plain old new IFaceImpl(); for now.
JAX-WS and Guice require a specific integration via #GuiceManaged annotation. More info here.
Move the business logic to a separate bean and annotate it with #Configurable so that Spring can handle the lifecycle of the bean. Now use that bean in NoteStorageWS
#Configurable
public class NoteStorageUtil{
#Named("NoteDAO")
#Inject
private NoteDAO noteDAO;
public StorageState takeNote(String note) {
try {
l.info(format("Service received message: '%s'", note));
Note n = new Note();
n.setContent(note);
noteDAO.store(n);
} catch (Exception e) {
l.error(e);
return StorageState.FAILURE;
}
return StorageState.SUCCESS;
}
}
#WebService(name = "NoteStorage", serviceName = "NoteStorageWS")
public class NoteStorageWS implements NoteStore {
public StorageState takeNote(String note) {
return new NoteStorageUtil().takeNote(note)
}
}
Or please check if your end point configuration is proper, so that the web service end point is a spring-managed bean also.
Eg:-
<bean id="hello" class="demo.spring.service.HelloWorldImpl" />
<jaxws:endpoint id="helloWorld" implementor="#hello" address="/HelloWorld" />
check the link

Resources