WebService with Apache CXF and custom headers - java

I created a web service using Apache cfx and spring, it works, but I need that the response include this header
<?xml version="1.0" encoding="UTF-8"?>
Right now the response is like this.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:postEncuestaResponse xmlns:ns2="http://webservice.atomsfat.com/">
<respuestaEncuesta>
<dn>12315643154</dn>
<encuestaPosted>true</encuestaPosted>
<fecha>2009-09-30T16:32:33.163-05:00</fecha>
</respuestaEncuesta>
</ns2:postEncuestaResponse>
</soap:Body>
</soap:Envelope>
But should be like this
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:postEncuestaResponse xmlns:ns2="http://webservice.atomsfat.com/">
<respuestaEncuesta>
<dn>12315643154</dn>
<encuestaPosted>true</encuestaPosted>
<fecha>2009-09-30T16:32:33.163-05:00</fecha>
</respuestaEncuesta>
</ns2:postEncuestaResponse>
</soap:Body>
</soap:Envelope>
This is the configuration of the beans of spring that expose the service.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxws:endpoint
id="encuestas"
implementor="webservice.serviceImpl"
address="/Encuestas" >
</jaxws:endpoint>
</beans>
this is the interface
import java.util.List;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
#WebService
public interface Encuestas {
#WebResult(name= "respuestaEncuesta")
RespuestaEncuestaMsg postEncuesta (#WebParam(name = "encuestaMsg") EncuestaMsg message);
}
Any ideas ?

Or use CXF build-in configuration capabilities.
Just add this IN your CXF Spring config :
<jaxws:properties>
<entry key="org.apache.cxf.stax.force-start-document">
<bean class="java.lang.Boolean">
<constructor-arg value="true"/>
</bean>
</entry>
</jaxws:properties>

Check the following
How can I add soap headers to the request/response?
Adding JAX-WS handlers to web services
Converting JAX-WS handlers to Apache CXF interceptors
then decide for one of the options and implement a handler/interceptor which adds what you need.

Well I implement a Handler, first I downloaded the examples from CXF and modified the logging Handler, and it works.
The configuration of spring :
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxws:endpoint
id="encuestas"
implementor="com.webservice.EncuestasImpl"
address="/Encuestas">
<jaxws:handlers>
<bean class="com.webservice.HeaderHandler"/>
</jaxws:handlers>
</jaxws:endpoint>
And the code this is the code for the handler.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.webservice;
import java.io.PrintStream;
import java.util.Map;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
/*
* This simple logical Handler will output the payload of incoming
* and outgoing messages.
*/
public class HeaderHandler implements SOAPHandler<SOAPMessageContext> {
private PrintStream out;
public HeaderHandler() {
setLogStream(System.out);
}
protected final void setLogStream(PrintStream ps) {
out = ps;
}
public void init(Map c) {
System.out.println("LoggingHandler : init() Called....");
}
public Set<QName> getHeaders() {
return null;
}
public boolean handleMessage(SOAPMessageContext smc) {
System.out.println("LoggingHandler : handleMessage Called....");
logToSystemOut(smc);
return true;
}
public boolean handleFault(SOAPMessageContext smc) {
System.out.println("LoggingHandler : handleFault Called....");
logToSystemOut(smc);
return true;
}
// nothing to clean up
public void close(MessageContext messageContext) {
System.out.println("LoggingHandler : close() Called....");
}
// nothing to clean up
public void destroy() {
System.out.println("LoggingHandler : destroy() Called....");
}
/*
* Check the MESSAGE_OUTBOUND_PROPERTY in the context
* to see if this is an outgoing or incoming message.
* Write a brief message to the print stream and
* output the message. The writeTo() method can throw
* SOAPException or IOException
*/
protected void logToSystemOut(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean)
smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
out.println("\nOutbound message:");
} else {
out.println("\nInbound message:");
}
SOAPMessage message = smc.getMessage();
try {
message.setProperty(SOAPMessage.WRITE_XML_DECLARATION, "true");
message.writeTo(out);
out.println();
} catch (Exception e) {
out.println("Exception in handler: " + e);
}
}
}
Note: that in the try I use MessageContext.MESSAGE_OUTBOUND_PROPERTY like Justin suggested.

Folowing the links provided by jitter, I went to http://cxf.apache.org/faq.html#FAQ-HowcanIaddsoapheaderstotherequest%252Fresponse%253F and found the following solution:
// My object of the custom header
AuthenticationHeader aut = new AuthenticationHeader();
aut.setUserName("ws");
aut.setPassword("ws123");
IntegrationWS integration = new IntegrationWS();
List<Header> headers = new ArrayList<Header>();
Header dummyHeader;
try {
dummyHeader = new Header(new QName("http://www.company.com/ws/", "AuthenticationHeader"), auth, new JAXBDataBinding(AuthenticationHeader.class));
} catch (JAXBException e) {
throw new IllegalStateException(e);
}
headers.add(dummyHeader);
IntegrationWSSoap soapPort = integration.getIntegrationWSSoap12();
//client side:
((BindingProvider)soapPort).getRequestContext().put(Header.HEADER_LIST, headers);
ArrayOfBrand arrayBrand = soapPort.syncBrands();

I don't have Apache CXF specific knowledge, but the jax-ws way to add the xml declaration seems to be to make a handler and use SOAPMessage.setProperty() to turn that feature on:
message.setProperty(SOAPMessage.WRITE_XML_DECLARATION, "true");
You should be able to add a jax-ws handler to your endpoint by adding a jaxws:handlers element in that spring config.

Related

Spring MVC Will Not Map to My Controller

I am trying to migrate a project from a traditional (working) Java servlet application to Spring MVC in the NetBeans IDE, but my program absolutely refuses to ping the Controller. On the client side, I see the following 404 errors:
index.do?displayType=table:1 Failed to load resource: the server responded with a status of 404 (Not Found)
index.do?displayType=table:1 Failed to load resource: the server responded with a status of 404 (Not Found)
I invoke the controller methods like so:
$.get("index.do","displayType=table", function( data ) {
jstring = JSON.parse(data.substr(12).slice(0, -1));
});
$.get("index.do","displayType=pivot",function(unparsedJSON) {});
$.post("index.do","jsonString=" + JSON.stringify(hot.getData()));
Below are my controller and xml configuration files:
MappingControl.java
package controllers;
import infoLoader.JsonWriter;
import infoLoader.getJSON;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.springframework.http.HttpMethod.GET;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
#Controller
#RequestMapping("/")
public class MappingControl {
#RequestMapping(value="/index.do", method=RequestMethod.GET)
public String populatePivotAndSheet(#RequestParam("displayType") String type) {
String returnedJSON = "";
try {
returnedJSON = getJSON.getJSON(type);
} catch (Exception ex) {
System.out.println("Unable to retrieve JSON");
}
return returnedJSON;
}
#RequestMapping(value="/index.do", method=RequestMethod.POST)
public void deliverSheet(#RequestParam("jsonString") String writableJSON) {
String returnedJSON = "";
JsonWriter.writeJSON(writableJSON);
}
}
applicationContext.xml
<?xml version='1.0' encoding='UTF-8' ?>
<!-- was: <?xml version="1.0" encoding="UTF-8"?> -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
</beans>
dispatcher-servlet.xml
<?xml version='1.0' encoding='UTF-8' ?>
<!-- was: <?xml version="1.0" encoding="UTF-8"?> -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" xmlns:mvc="http://www.springframework.org/schema/mvc">
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<!--
Most controllers will use the ControllerClassNameHandlerMapping above, but
for the index controller we are using ParameterizableViewController, so we must
define an explicit mapping for it.
-->
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="index.htm">indexController</prop>
</props>
</property>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
<!--
The index controller.
-->
<bean name="indexController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController"
p:viewName="index" />
<mvc:resources mapping="/resources/**" location="/resources" />
<context:component-scan base-package="controllers" />
</beans>
I've been building this based on the default Spring-MVC template provided by NetBeans, so if any of you believe the template is poorly formatted and should be changed in some way, I would appreciate any input you might have.
Thanks so much for your time, guys, and let me know if anything is unclear.
You need add #ResponseBody to your method,due to in your case,you want to return the json data,if you missing #ResponseBody it will return to the view page,however you do not specify any view page in your code,thus will cause 404 error
#ResponseBody
#RequestMapping(value="/index.do", method=RequestMethod.GET)
public String populatePivotAndSheet(#RequestParam("displayType") String type) {
String returnedJSON = "";
try {
returnedJSON = getJSON.getJSON(type);
} catch (Exception ex) {
System.out.println("Unable to retrieve JSON");
}
return returnedJSON;
}
#ResponseBody
#RequestMapping(value="/index.do", method=RequestMethod.POST)
public void deliverSheet(#RequestParam("jsonString") String writableJSON) {
String returnedJSON = "";
JsonWriter.writeJSON(writableJSON);
}

Default RequestMapping does not get invoked

I am stuck with a strange situation when loading up my web application on tomcat. The welcome file gets loaded well, but the controller part is not getting invoked.
Here is my Project Directory:
Here is my web.xml
<?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">
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/application-context.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/WEB-INF/views/index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Here is my controller class
package com.bng.monitor.controller;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.bng.monitor.util.Utility;
#Controller
public class MonitoringController {
private static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
private static SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//private static List<DBObject> pipeline=new ArrayList<DBObject>();
static HashMap<String, String> hmLatestDateTime=new HashMap<String, String>();
#Autowired
private Utility utility;
private static #Value("${pageRefresh}") String pageRefresh;
public Utility getUtility() {
return utility;
}
public void setUtility(Utility utility) {
this.utility = utility;
}
public static String getPagerefresh() {
return pageRefresh;
}
public void setPagerefresh(String pagerefresh) {
MonitoringController.pageRefresh = pagerefresh;
}
#RequestMapping(value="/version", method=RequestMethod.GET)
public ModelAndView getVersion(){
System.out.println("inside controller..");
ModelAndView mav=null;
try
{
mav=new ModelAndView("version");
if(Utility.version!=null)
mav.addObject("versionapp", Utility.version);
else if(Utility.version == null || Utility.version.equals(""))
mav.addObject("versionapp", "property not defined.. :)");
} catch (Exception e) {
e.printStackTrace();
}
return mav;
} // end of getVersion()
#RequestMapping(value="/reloadftp",method=RequestMethod.GET)
public void reloadFtpConfig(){
try {
this.utility.getFtpCred();
} catch (Exception e) {
e.printStackTrace();
}
}
#RequestMapping(value="/")
public String displayGui(ModelMap model) {
try
{
System.out.println("<<< inside root mapping for index.jsp display >>>");
model.addAttribute("map", Utility.hmServerFtpCred);
model.addAttribute("pagerefresh", pageRefresh);
String key=null;
String combinedJson=null;
JSONArray jsonArray=null;
JSONObject jsonObject1=null;
JSONObject jsonObject2=null;
String tempMod=null;
String tempStat=null;
String cpu=null;
String ram=null;
for(Map.Entry entry: Utility.hmServerFtpCred.entrySet()){
key = (String) entry.getKey();
// for each key get combinedJson (and do so in a loop as the json formed there may not be valid) and parse it to get module and system properties cached into hmGuiData
//TODO add logic to alter GUI to give some indication(color based) that the data is not updated since long time.
combinedJson = Utility.getStats(key);
jsonArray = new JSONArray(combinedJson);
System.out.println(">>> combined JSON: "+jsonArray.toString());
JSONArray moduleArr = jsonArray.getJSONObject(0).getJSONObject("details").getJSONArray("module");
for(int index=0; index<moduleArr.length() ; index++)
{
tempMod=moduleArr.getJSONObject(index).getString("module_name");
tempStat=moduleArr.getJSONObject(index).getString("live");
Utility.hmGuiData.put(key+"_"+tempMod.toLowerCase(), tempStat);
}
Utility.hmGuiData.put(key+"_"+"cpu", jsonArray.getJSONObject(1).getJSONObject("details").getString("cpu"));
Utility.hmGuiData.put(key+"_"+"ram", jsonArray.getJSONObject(1).getJSONObject("details").getString("ram"));
/*jsonObject1 = (JSONObject) jsonArray.get(0);
jsonObject2 = (JSONObject) jsonArray.get(1);
jsonObject1.getJSONObject("details").*/
} // end of inner for
System.out.println(">>> Utility.hmGuiData: "+Utility.hmGuiData);
model.addAttribute("guidata", Utility.hmGuiData);
return "index";
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#RequestMapping(value="/getstatsone",method=RequestMethod.GET)
public void getStatsOne(HttpServletRequest req,HttpServletResponse resp){
}
#RequestMapping(value="/getopco",method=RequestMethod.GET)
public void getOpco(HttpServletRequest req,HttpServletResponse resp) {
PrintWriter pw=null;
System.out.println(">>> inside /getopco");
JSONObject json=null;
try
{
pw=resp.getWriter();
json=new JSONObject(Utility.hmServerFtpCred);
pw.println(json);
}
catch (Exception e)
{
e.printStackTrace();
}finally{
try {
if(pw!=null)
pw.close();
} catch (Exception ee) {
ee.printStackTrace();
}
}
}
}//end of Controller class
and here is my application-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:core="http://activemq.apache.org/schema/core" xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.7.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:component-scan base-package="com.bng.monitor" />
<bean id="properties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/config.properties" />
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<mvc:resources location="/resources/css/**" mapping="/css/**"/>
<mvc:resources location="/resources/img/**" mapping="/img/**"/>
<mvc:resources location="/resources/js/**" mapping="/js/**"/>
<!-- <mvc:resources location="/WEB-INF/views/**" mapping="/js/**"/> -->
<mvc:annotation-driven />
<mvc:resources mapping="/*" location="/WEB-INF/views/" />
</beans>
Now there are two things happening...
1. When I make the url pattern as /* and make the request, then only the controller gets invoked, welcome jsp is not loaded and tomcat gives error 404, the requested resource is not available.
When i change the url pattern to /, then only welcome page gets loaded and no backend controller specific mapping is called.
In both the cases, my application is not loaded.
Please help me out to load the web app successfully.
Removing the following welcome list from web.xml solved the issue.
So, I just removed the following lines from web.xml
<welcome-file-list>
<welcome-file>/WEB-INF/views/index.jsp</welcome-file>
</welcome-file-list>

How to use a spring bean within SOAPHandler

I just want to use a spring bean within handleMessage() method which is called in handler-chain.xml.
This is my handler-chain.xml:
<jaxws:bindings>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<!-- ====================== -->
<!-- service based handlers -->
<!-- ====================== -->
<handler-chain>
<handler>
<handler-name>CustomerHandler</handler-name>
<handler-class>com.test.ws.handler.CustomerHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>
</jaxws:bindings>
And also this is my CustomerHandler.java class:
public class CustomerHandler implements SOAPHandler<SOAPMessageContext> {
#Autowired
public ServiceInvokeUtil serviceInvokeUtil;
public Set<QName> getHeaders() {
return null;
}
public void close(MessageContext context) {
}
public boolean handleFault(SOAPMessageContext context) {
logSoapMessage(context);
return false;
}
public boolean handleMessage(SOAPMessageContext context) {
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
SOAPMessage soapMsg = context.getMessage();
// if this is a request, true for outbound messages, false for inbound
if (isRequest) {
try {
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();
// if no header, add one
if (soapHeader == null) {
soapHeader = soapEnv.addHeader();
}
Boolean oa = serviceInvokeUtil.createRecord(SoapUtil.getRecordEntity());
SOAPElement userContextHeader = ConsumerHeaderHelper.createUserContextHeader(context);
soapHeader.addChildElement(userContextHeader);
}
soapMsg.saveChanges();
} catch (SOAPException e) {
System.err.println(e);
}
} else {
try {
SoapUtil.setSoapResponseHeader(soapMsg.getSOAPHeader());
} catch (SOAPException e) {
e.printStackTrace();
}
}
logSoapMessage(context);
return true;
}
This is my spring configuration xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">
<context:property-placeholder />
<bean id="serviceInvokeUtil" class="com.test.ws.util.ServiceInvokeUtil">
</bean>
When I am calling a web service, the spring bean serviceInvokeUtil is null.
How to use serviceInvokeUtil class as a spring bean within handleMessage method?
I extended SpringBeanAutowiringSupport to CustomerHandler class than it was solved. Like this: public class CustomerHandler extends SpringBeanAutowiringSupport implements SOAPHandler {.....
Spring will processes #Autowired annotation only if it will control creation of instance. In other words, your CustomerHandler have to be bean as well as ServiceInvokeUtil to force spring autowire it.

Spring Rest and Jsonp

I am trying to get my Spring rest controller to return jsonp but I am having no joy
The exact same code works ok if I want to return json but I have a requirement to return jsonp
I have added in a converter I found source code for online for performing the jsonp conversion
I am using Spring version 4.1.1.RELEASE and Java 7
Any help is greatly appreciated
Here is the code in question
mvc-dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="favorParameter" value="true" />
<property name="parameterName" value="mediaType" />
<property name="ignoreAcceptHeader" value="false"/>
<property name="useJaf" value="false"/>
<property name="defaultContentType" value="application/json" />
<property name="mediaTypes">
<map>
<entry key="atom" value="application/atom+xml" />
<entry key="html" value="text/html" />
<entry key="jsonp" value="application/javascript" />
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml"/>
</map>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="contentNegotiationManager" ref="contentNegotiationManager" />
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/templates/slim/${views.template.directory}/" />
<property name="suffix" value=".jsp" />
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="com.webapp.handler.MappingJacksonJsonpView" />
</list>
</property>
</bean>
</beans>
com.webapp.handler.MappingJacksonJsonpView
package com.webapp.handler;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
public class MappingJacksonJsonpView extends MappingJackson2JsonView {
/** Local log variable. **/
private static final Logger LOG = LoggerFactory.getLogger(MappingJacksonJsonpView.class);
/**
* Default content type. Overridable as bean property.
*/
public static final String DEFAULT_CONTENT_TYPE = "application/javascript";
#Override
public String getContentType() {
return DEFAULT_CONTENT_TYPE;
}
/**
* Prepares the view given the specified model, merging it with static
* attributes and a RequestContext attribute, if necessary.
* Delegates to renderMergedOutputModel for the actual rendering.
* #see #renderMergedOutputModel
*/
#Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
LOG.info("Entered render Method :{}", request.getMethod());
if("GET".equals(request.getMethod().toUpperCase())) {
LOG.info("Request Method is a GET call");
Map<String, String[]> params = request.getParameterMap();
if(params.containsKey("callback")) {
String callbackParam = params.get("callback")[0];
LOG.info("callbackParam:{}", callbackParam);
response.getOutputStream().write(new String(callbackParam + "(").getBytes());
super.render(model, request, response);
response.getOutputStream().write(new String(");").getBytes());
response.setContentType(DEFAULT_CONTENT_TYPE);
}
else {
LOG.info("Callback Param not contained in request");
super.render(model, request, response);
}
}
else {
LOG.info("Request Method is NOT a GET call");
super.render(model, request, response);
}
}
}
Controller Method In Question
#RequestMapping(value = { "/sources"}, method = RequestMethod.GET,
produces={MediaType.ALL_VALUE,
"text/javascript",
"application/javascript",
"application/ecmascript",
"application/x-ecmascript",
"application/x-javascript",
MediaType.APPLICATION_JSON_VALUE})
#ResponseBody
public Object getSources(#PathVariable(value = API_KEY) String apiKey,
#RequestParam(value = "searchTerm", required = true) String searchTerm,
#RequestParam(value = "callBack", required = false) String callBack) {
LOG.info("Entered getSources - searchTerm:{}, callBack:{} ", searchTerm, callBack);
List<SearchVO> searchVOList = myServices.findSources(searchTerm);
if (CollectionUtils.isEmpty(searchVOList)) {
LOG.error("No results exist for the searchterm of {}", searchTerm);
return searchVOList;
}
LOG.debug("{} result(s) exist for the searchterm of {}", searchVOList.size(), searchTerm);
LOG.info("Exiting getSources");
return searchVOList;
}
**Jquery Ajax Code **
$.ajax({
type: "GET",
url: "http://localhost:8080/my-web/rest/sources,
data: {
"searchTerm": request.term
},
//contentType: "application/json; charset=utf-8",
//dataType: "json",
contentType: "application/javascript",
dataType: "jsonp",
success: function(data) {
alert("success");
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert("Failure");
}
});
A snippet of the error stacktrace that I get is as follows
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:168) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:198) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71) ~[spring-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:620) [servlet-api.jar:na]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) [servlet-api.jar:na]
As stated on the spring.io blog regarding the Spring 4.1 release:
JSONP is now supported with Jackson. For response body methods declare
an #ControllerAdvice as shown below. For View-based rendering simply
configure the JSONP query parameter name(s) on
MappingJackson2JsonView.
#ControllerAdvice
private static class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
[...] In 4.1 an #ControllerAdvice can also implement
ResponseBodyAdvice in which case it will be called after the
controller method returns but before the response is written and
therefore committed. This has a number of useful applications with
#JsonView the JSONP already serving as two examples built on it.
Javadoc taken from MappingJackson2JsonView:
Set JSONP request parameter names. Each time a request has one of those
parameters, the resulting JSON will be wrapped into a function named as
specified by the JSONP request parameter value.
The parameter names configured by default are "jsonp" and "callback".
You don't need to implement this stuff by yourself. Just reuse the bits from the Spring Framework.
Spring Boot example
Following simple Spring Boot application demonstrates use of build in JSONP support in Spring MVC 4.1.
Example requires at least Spring Boot 1.2.0.RC1.
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;
import java.util.Collections;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
#RestController
#SpringBootApplication
class Application {
#JsonAutoDetect(fieldVisibility = ANY)
static class MyBean {
String attr = "demo";
}
#ControllerAdvice
static class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
#Bean
public HttpMessageConverters customConverters() {
return new HttpMessageConverters(false, Collections.<HttpMessageConverter<?> >singleton(new MappingJackson2HttpMessageConverter()));
}
#RequestMapping
MyBean demo() {
return new MyBean();
}
#RequestMapping(produces = APPLICATION_JSON_VALUE)
String demo2() {
return "demo2";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
URL http://localhost:8080/demo?callback=test converts a POJO into a JSONP response:
test({"attr":"demo"});
URL http://localhost:8080/demo2?callback=test converts a String into a JSONP response:
test("demo2");

Is there a PropertyPlaceholderConfigurer-like class for use with Spring that accepts XML?

Spring has a very handy convenience class called PropertyPlaceholderConfigurer, which takes a standard .properties file and injects values from it into your bean.xml config.
Does anyone know of a class which does exactly the same thing, and integrates with Spring in the same way, but accepts XML files for the config. Specifically, I'm thinking of Apache digester-style config files. It would be easy enough to do this, I'm just wondering if anyone already has.
Suggestions?
I just tested this, and it should just work.
PropertiesPlaceholderConfigurer contains a setPropertiesPersister method, so you can use your own subclass of PropertiesPersister. The default PropertiesPersister already supports properties in XML format.
Just to show you the fully working code:
JUnit 4.4 test case:
package org.nkl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#ContextConfiguration(locations = { "classpath:/org/nkl/test-config.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
public class PropertyTest {
#Autowired
private Bean bean;
#Test
public void testPropertyPlaceholderConfigurer() {
assertNotNull(bean);
assertEquals("fred", bean.getName());
}
}
The spring config file test-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
">
<context:property-placeholder
location="classpath:/org/nkl/properties.xml" />
<bean id="bean" class="org.nkl.Bean">
<property name="name" value="${org.nkl.name}" />
</bean>
</beans>
The XML properties file properties.xml - see here for description of usage.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="org.nkl.name">fred</entry>
</properties>
And finally the bean:
package org.nkl;
public class Bean {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Hope this helps...
Found out that Spring Modules provide integration between Spring and Commons Configuration, which has a hierarchial XML configuration style. This ties straight into PropertyPlaceholderConfigurer, which is exactly what I wanted.
Been trying to come up with a nice solution to this myself that
Revolves around creating an XSD for the config file - since in my mind the whole benefit of using XML is that you can strongly type the config file, in terms of datatypes, and which fields are mandatory/optional
Will validate the XML against the XSD, so if a value is missing it'll throw an error out rather than your bean being injected with a 'null'
Doesn't rely on spring annotations (like #Value - in my mind that's giving beans knowledge about their container + relationship with other beans, so breaks IOC)
Will validate the spring XML against the XSD, so if you try to reference an XML field not present in the XSD, it'll throw out an error too
The bean has no knowledge that its property values are being injected from XML (i.e. I want to inject individual properties, and not the XML object as a whole)
What I came up with is as below, apologies this is quite long winded, but I like it as a solution since I believe it covers everything. Hopefully this might prove useful to someone. Trivial pieces first:
The bean I want property values injected into:
package com.ndg.xmlpropertyinjectionexample;
public final class MyBean
{
private String firstMessage;
private String secondMessage;
public final String getFirstMessage ()
{
return firstMessage;
}
public final void setFirstMessage (String firstMessage)
{
this.firstMessage = firstMessage;
}
public final String getSecondMessage ()
{
return secondMessage;
}
public final void setSecondMessage (String secondMessage)
{
this.secondMessage = secondMessage;
}
}
Test class to create the above bean and dump out the property values it got:
package com.ndg.xmlpropertyinjectionexample;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Main
{
public final static void main (String [] args)
{
try
{
final ApplicationContext ctx = new ClassPathXmlApplicationContext ("spring-beans.xml");
final MyBean bean = (MyBean) ctx.getBean ("myBean");
System.out.println (bean.getFirstMessage ());
System.out.println (bean.getSecondMessage ());
}
catch (final Exception e)
{
e.printStackTrace ();
}
}
}
MyConfig.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:myconfig="http://ndg.com/xmlpropertyinjectionexample/config" targetNamespace="http://ndg.com/xmlpropertyinjectionexample/config">
<xsd:element name="myConfig">
<xsd:complexType>
<xsd:sequence>
<xsd:element minOccurs="1" maxOccurs="1" name="someConfigValue" type="xsd:normalizedString" />
<xsd:element minOccurs="1" maxOccurs="1" name="someOtherConfigValue" type="xsd:normalizedString" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Sample MyConfig.xml file based on the XSD:
<?xml version="1.0" encoding="UTF-8"?>
<config:myConfig xmlns:config="http://ndg.com/xmlpropertyinjectionexample/config">
<someConfigValue>First value from XML file</someConfigValue>
<someOtherConfigValue>Second value from XML file</someOtherConfigValue>
</config:myConfig>
Snippet of pom.xml file to run xsd2java (wasn't much else in here besides setting to Java 1.6, and spring-context dependency):
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<executions>
<execution>
<id>main-xjc-generate</id>
<phase>generate-sources</phase>
<goals><goal>generate</goal></goals>
</execution>
</executions>
</plugin>
Now the spring XML itself. This creates a schema/validator, then uses JAXB to create an unmarshaller to create a POJO from the XML file, then uses spring # annotation to inject property values by quering the POJO:
<?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-3.2.xsd" >
<!-- Set up schema to validate the XML -->
<bean id="schemaFactory" class="javax.xml.validation.SchemaFactory" factory-method="newInstance">
<constructor-arg value="http://www.w3.org/2001/XMLSchema"/>
</bean>
<bean id="configSchema" class="javax.xml.validation.Schema" factory-bean="schemaFactory" factory-method="newSchema">
<constructor-arg value="MyConfig.xsd"/>
</bean>
<!-- Load config XML -->
<bean id="configJaxbContext" class="javax.xml.bind.JAXBContext" factory-method="newInstance">
<constructor-arg>
<list>
<value>com.ndg.xmlpropertyinjectionexample.config.MyConfig</value>
</list>
</constructor-arg>
</bean>
<bean id="configUnmarshaller" class="javax.xml.bind.Unmarshaller" factory-bean="configJaxbContext" factory-method="createUnmarshaller">
<property name="schema" ref="configSchema" />
</bean>
<bean id="myConfig" class="com.ndg.xmlpropertyinjectionexample.config.MyConfig" factory-bean="configUnmarshaller" factory-method="unmarshal">
<constructor-arg value="MyConfig.xml" />
</bean>
<!-- Example bean that we want config properties injected into -->
<bean id="myBean" class="com.ndg.xmlpropertyinjectionexample.MyBean">
<property name="firstMessage" value="#{myConfig.someConfigValue}" />
<property name="secondMessage" value="#{myConfig.someOtherConfigValue}" />
</bean>
</beans>
I'm not sure about the Apache digester-style config files, but I found a solution that was not that hard to implement and suitable for my xml config-file.
You can use the normal PropertyPlaceholderConfigurer from spring, but to read your custom config you have to create your own PropertiesPersister, where you can parse the xml (with XPath) and set the required properties yourself.
Here's a small example:
First create your own PropertiesPersister by extending the default one:
public class CustomXMLPropertiesPersister extends DefaultPropertiesPersister {
private XPath dbPath;
private XPath dbName;
private XPath dbUsername;
private XPath dbPassword;
public CustomXMLPropertiesPersister() throws JDOMException {
super();
dbPath = XPath.newInstance("//Configuration/Database/Path");
dbName = XPath.newInstance("//Configuration/Database/Filename");
dbUsername = XPath.newInstance("//Configuration/Database/User");
dbPassword = XPath.newInstance("//Configuration/Database/Password");
}
public void loadFromXml(Properties props, InputStream is)
{
Element rootElem = inputStreamToElement(is);
String path = "";
String name = "";
String user = "";
String password = "";
try
{
path = ((Element) dbPath.selectSingleNode(rootElem)).getValue();
name = ((Element) dbName.selectSingleNode(rootElem)).getValue();
user = ((Element) dbUsername.selectSingleNode(rootElem)).getValue();
password = ((Element) dbPassword.selectSingleNode(rootElem)).getValue();
}
catch (JDOMException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
props.setProperty("db.path", path);
props.setProperty("db.name", name);
props.setProperty("db.user", user);
props.setProperty("db.password", password);
}
public Element inputStreamToElement(InputStream is)
{
...
}
public void storeToXml(Properties props, OutputStream os, String header)
{
...
}
}
Then inject the CustomPropertiesPersister to the PropertyPlaceholderConfigurer in the application context:
<beans ...>
<bean id="customXMLPropertiesPersister" class="some.package.CustomXMLPropertiesPersister" />
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_FALLBACK" />
<property name="location" value="file:/location/of/the/config/file" />
<property name="propertiesPersister" ref="customXMLPropertiesPersister" />
</bean>
</beans>
After that you can use your properties like this:
<bean id="someid" class="some.example.class">
<property name="someValue" value="$(db.name)" />
</bean>

Categories

Resources