Spring Rest and Jsonp - java

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");

Related

How to call method after finishing first transaction in spring mvc

In my Spring mvc application, calling following method , when click 'Save' button in jsp page.
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addUser(#ModelAttribute("user") #Valid User u,
BindingResult result, #ModelAttribute("category") UserCategory uc,
BindingResult resultCat, Model model, RedirectAttributes reDctModel) {
this.userService.addUser(u); // adding new user to DB
reDctModel.addFlashAttribute("msgSucess","Successfully saved..!");
this.sendEmail(u.getUsr_email(),"RDMS","Login Details"); // For sending mail
return "redirect:/users";
}
public String sendEmail(String recipientAddress,String subject,String message) {
// creates a simple e-mail object
SimpleMailMessage email = new SimpleMailMessage();
email.setTo(recipientAddress);
email.setSubject(subject);
email.setText(message);
// sends the e-mail
this.mailSender.send(email);
return "Result";
}
This is my applicationcontext
</bean>
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="smtp.gmail.com" />
<property name="port" value="587" />
<property name="username" value="myemailaddress#gmail.com" />
<property name="password" value="********" />
<property name="javaMailProperties">
<props>
<prop key="mail.transport.protocol">smtp</prop>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
</props>
</property>
</bean>
The issue is, after adding sendEmail() ,it takes around 15 seconds to save the new record.Before adding this method it takes only 1 second.Can anybody guide me to reducing slowness of the programme or calling sendEmail() after finishing first transaction.Thanks in advance.
You have to create async service and configure your application for asynchronous usage, here you have spring tutorial. In async service you have to place your code for sending email. Below you can see sample code for 2 classes first is service second is application class,
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
#Service
public class SampleService {
#Async
public Future sendEmail(String email) {
// here you can place your code for sending email
return new AsyncResult("email sent successful");
}
}
import java.util.concurrent.Future;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
#SpringBootApplication
#EnableAsync
public class Application implements CommandLineRunner {
#Autowired
SampleService sampleService;
#Override
public void run(String... args) throws Exception {
Future page1 = sampleService.sendEmail("test#gmail.com");
while (!page1.isDone()) {
Thread.sleep(10);
}
System.out.println(page1.get());
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

IllegalStateException: Cannot convert value of type to required type

Here's the error I'm receiving.
Caused by: java.lang.IllegalStateException: Cannot convert value of type [code.ProductFieldSetMapper] to required type [org.springframework.batch.item.file.mapping.FieldSetMapper] for property 'FieldSetMapper': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:264)
at org.springframework.beans.BeanWrapperImpl.convertIfNecessary(BeanWrapperImpl.java:450)
... 23 more
Here's my context file (FileReaderConfig.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:batch="http://www.springframework.org/schema/batch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch.xsd">
<bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="file:./output.txt" />
<property name="linesToSkip" value="1" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="PRODUCT_ID,NAME,DESCRIPTION,PRICE" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="code.ProductFieldSetMapper" />
</property>
</bean>
</property>
</bean>
<job id="importProducts" xmlns="http://www.springframework.org/schema/batch">
<step id="readWriteProducts">
<tasklet>
<chunk reader="reader" writer="writer" commit-interval="100" />
</tasklet>
</step>
</job>
Here's the interface (FieldSetMapper.java)
package code;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
public interface FieldSetMapper<T> {
T mapFieldSet(FieldSet fieldSet) throws BindException;
}
Here's ProductFieldSetMapper.java
package code;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
public class ProductFieldSetMapper implements FieldSetMapper<Product> {
public Product mapFieldSet(FieldSet fieldSet) throws BindException {
// TODO Auto-generated method stub
Product product = new Product();
product.setId(fieldSet.readString("PRODUCT_ID"));
product.setName(fieldSet.readString("NAME"));
product.setDescription(fieldSet.readString("DESCRIPTION"));
product.setPrice(fieldSet.readBigDecimal("PRICE"));
return product;
}
}
And here's the class that I'm running (Runner.java)
package code;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.validation.BindException;
public class Runner {
public static void main(String[] args) throws BeansException, BindException {
// TODO Auto-generated method stub
Product product;
ApplicationContext context =
new ClassPathXmlApplicationContext("FileReaderConfig.xml");
ProductFieldSetMapper obj = (ProductFieldSetMapper) context.getBean("FieldSetMapper");
product = (Product) obj.mapFieldSet((FieldSet)context.getBean("lineTokenizer"));
System.out.println(product.getDescription() + ""+product.getId()+""+product.getName());
}
}
I don't see where (or why for that matter)my code is attempting to convert a ProductFieldSetMapper into a FieldSetMapper (which is just an interface, I understand that won't work).
BTW, Product.java is a POJO with variables and their respective setters and getters.
The error was the result of me using my own interface rather than the one provided by Spring. I deleted my interface class and had ProductFieldSetMapper implement org.springframework.batch.item.file.mapping.FieldSetMapper after importing it. That solved the issue.
ProductFieldSetMapper obj =
(ProductFieldSetMapper) context.getBean("FieldSetMapper");
Should be
ProductFieldSetMapper obj =
(ProductFieldSetMapper) context.getBean("fieldSetMapper");
See your bean declaration.
<property name="fieldSetMapper">
<bean class="code.ProductFieldSetMapper" />
</property>
Here is code with some correction:
Runner.java (use DelimitedLineTokenizer class to tokenize a comma separated string into FieldSet that is further used to map it with an object (Product) via ProductFieldSetMapper class)
ApplicationContext context = new ClassPathXmlApplicationContext(
"FileReaderConfig.xml");
ProductFieldSetMapper obj = (ProductFieldSetMapper) context.getBean("fieldSetMapper");
DelimitedLineTokenizer tokenizer = (DelimitedLineTokenizer) context
.getBean("lineTokenizer");
FieldSet fieldSet = tokenizer.tokenize("1,Pepsi,Cold drinks,30");
Product product = (Product) obj.mapFieldSet(fieldSet);
System.out.println(product.getDescription() + "-" + product.getId() + "-"
+ product.getName());
Config xml file: (No need to declare any beans or jobs other than two defined below because you are not using it anywhere in you Main class)
<bean id="lineTokenizer"
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="PRODUCT_ID,NAME,DESCRIPTION,PRICE" />
</bean>
<bean id="fieldSetMapper" class="com.spring.batch.domain.ProductFieldSetMapper" />
ProductFieldSetMapper.java: (There is no need to define your custom FieldSetMapper)
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
public class ProductFieldSetMapper implements org.springframework.batch.item.file.mapping.FieldSetMapper<Product> {
public Product mapFieldSet(FieldSet fieldSet) throws BindException {
Product product = new Product();
product.setId(fieldSet.readString("PRODUCT_ID"));
product.setName(fieldSet.readString("NAME"));
product.setDescription(fieldSet.readString("DESCRIPTION"));
product.setPrice(fieldSet.readBigDecimal("PRICE"));
return product;
}
}
For a detailed sample please read it HERE with extra functionality using spring batch jobs.

Xstream marshaller not unmarshelling xml post body

I am trying to create a spring rest service. My servlet.xml is like following:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<!-- Database Configuration -->
<import resource="classpath:DataSource.xml"/>
<import resource="classpath:Hibernate.xml"/>
<!-- Beans Declaration -->
<import resource="classpath:Employee.xml"/>
<context:component-scan base-package="com.nilenium.service.controller" />
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter"/>
<ref bean="marshallingHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="autodetectAnnotations" value="true"/>
<property name="annotatedClasses">
<list>
<value>com.nilenium.employee.model.Employee</value>
<value>com.nilenium.employee.model.EmployeeRecord</value>
</list>
</property>
</bean>
<bean id="marshallingHttpMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="xstreamMarshaller"/>
<property name="unmarshaller" ref="xstreamMarshaller"/>
</bean>
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="ignoreAcceptHeader" value="true" />
<property name="defaultContentType" value="application/json" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
<property name="defaultViews">
<list>
<!-- JSON View -->
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
<!-- XML View -->
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg>
<bean class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="autodetectAnnotations" value="true"/>
<property name="aliases">
<props>
<prop key="employee">com.nilenium.employee.model.Employee</prop>
<prop key="employee">com.nilenium.employee.model.EmployeeRecord</prop>
</props>
</property>
</bean>
</constructor-arg>
</bean>
</list>
</property>
</bean>
</beans>
And my model class is like this:
package com.nilenium.employee.model;
import java.io.Serializable;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonRootName;
import com.thoughtworks.xstream.annotations.XStreamAlias;
#XStreamAlias("employee")
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private int employeeId;
private String employeeName;
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public Employee(int employeeId, String employeeName)
{
this.employeeId = employeeId;
this.employeeName = employeeName;
}
public Employee() {
}
}
My service controller class is like this:
package com.nilenium.service.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.nilenium.employee.bo.EmployeeBo;
import com.nilenium.employee.model.Employee;
import com.nilenium.employee.model.EmployeeRecord;
#Controller
#RequestMapping("/Service")
public class NileniumServiceController {
#Autowired
private EmployeeBo employeeBo;
#ExceptionHandler
public String exceptionHandler(Exception e){
return "serviceError";
}
#RequestMapping(value = "employeeId/{employeeId}", method = RequestMethod.GET, headers = "Accept=application/xml, application/json")
public Employee getEmployeeName(#PathVariable int employeeId,
HttpServletResponse resp,
ModelMap model) {
// System.out.println("Emp Name : " + employeeName);
Employee empReturned = new Employee();
empReturned.setEmployeeId(employeeId);
empReturned.setEmployeeName("Demo Name");
return empReturned;
}
#RequestMapping(value = "/employees", method = RequestMethod.GET, headers = "Accept=application/xml, application/json")
public EmployeeRecord getAllEmployeeDetails(HttpServletResponse resp,
ModelMap model) {
List<Employee> empListReturned = employeeBo.getAllEmployee();
EmployeeRecord eRecords = new EmployeeRecord();
eRecords.setEmployeeList(empListReturned);
return eRecords;
}
#RequestMapping(value = "/storeemployee",method = RequestMethod.POST, headers = {"Accept=text/xml, application/json"})
public Employee storeEmpRecord(#RequestBody Employee employee,HttpServletResponse resp,
ModelMap model)
{
System.out.println("Emp Id received : "+employee.getEmployeeId());
System.out.println("Emp Name received : "+employee.getEmployeeName());
// Employee empReturned = employeeBo.findByEmployeeId(1);
return employee;
}
}
When I am trying to post an xml to the storeEmpRecord method xstream marshaller is not able to convert the xml request to respective object (Employee object).
My test client is like this:
package com.nilenium.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry;
import com.nilenium.employee.model.Employee;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class RestPostTest {
public static void main(String[] args) {
Employee emp = new Employee();
emp.setEmployeeId(1);
emp.setEmployeeName("Nilalohita");
XStream xStream = new XStream(new DomDriver());
xStream.alias("employee", Employee.class);
System.out.println(xStream.toXML(emp));
String xmlBody = xStream.toXML(emp);
HttpClient client = new DefaultHttpClient();
String jsonDemo = "{\"employeeId\":2,\"employeeName\":\"He-Man\"}";
try {
HttpPost post = new HttpPost("http://localhost:8080/SampleMaven/Service/storeemployee");
// post.addHeader("Accept", "application/json");
// post.addHeader("Content-Type", "application/json");
post.addHeader("Accept", "application/xml");
post.addHeader("Content-Type", "application/xml");
StringEntity entity = new StringEntity(xmlBody, "UTF-8");
// entity.setContentType("application/json");
entity.setContentType("application/xml");
post.setEntity(entity);
HttpResponse response = client.execute(post);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = "";
while ((line = rd.readLine()) != null) {
System.out.println(line);
}
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Please let me know if i am doing something wrong. The same thing is working for jaxb using #XMLroot for Employee class instead of #XStreamAlias("employee").
I am getting the following error. the full stack trace is like this
Caused by:</h3><pre>javax.servlet.ServletException: Unable to locate object to be marshalled in model: {}
at org.springframework.web.servlet.view.xml.MarshallingView.renderMergedOutputModel(MarshallingView.java:100)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:250)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1047)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:817)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:648)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:205)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)
The problem is in the controller method :
#RequestMapping(value = "/storeemployee",method = RequestMethod.POST, headers = {"Accept=text/xml, application/json"})
public Employee storeEmpRecord(#RequestBody Employee employee,HttpServletResponse resp,
ModelMap model)
{
System.out.println("Emp Id received : "+employee.getEmployeeId());
System.out.println("Emp Name received : "+employee.getEmployeeName());
// Employee empReturned = employeeBo.findByEmployeeId(1);
return employee;
}
Spring MVC does not (cannot) add objects to model automatically. You need to add the employee object to the ModelMap. The model map is then passed to the marshaling view, which looks for any objects that it can marshal, and marshals them to response.
Unable to locate object to be marshalled in model: {} Means the Marshalling View received a ModelMap with no objects that have the XStream annotations.
Hope this helps.

Customize SpringSecurity OAuth 2 Error Output (unauthorized)

We secure our REST services (for server to server communication, no user involved) with Spring Security OAuth2. However when one tries to access a protected resource in a browser, it will show:
<oauth>
<error_description>
An Authentication object was not found in the SecurityContext
</error_description>
<error>unauthorized</error>
</oauth>
We want this to be a custom page of our own choosing. Is there a way?
Setting the access-denied-page won't do. For one it requires the definition of a login page which we don't have as this is a pure server to server communication. For another this attribute is supposedly deprecated since Spring 3.0.. or something.
Anyway.. Debugged my way into the OAuth Error Handling. And found that the response seems to somehow get enriched with the information I see on the error page. Apparently no page rendering at all is done so it looks like there is no error page to replace..?!
At least we want to hide the fact that we use OAuth and just display a basic "Denied" text if we can't have a "real" page.. So maybe I'll have to extend the spring security handler or add a custom filter to modify the response?!
Maybe a redirect to our error page?
Thanks!
Edit
For our current setup check my other SO question here
I had to remove the oauth detail too and my solution was to implement my own OAuth2ExceptionRenderer
package org.some.nice.code;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.oauth2.provider.error.OAuth2ExceptionRenderer;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
public class HeaderOnlyOAuth2ExceptionRenderer implements OAuth2ExceptionRenderer
{
private final Log logger = LogFactory
.getLog(MyOAuth2ExceptionRenderer.class);
public void handleHttpEntityResponse(HttpEntity<?> responseEntity,
ServletWebRequest webRequest) throws Exception
{
if (responseEntity == null)
{
return;
}
HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
logger.info("filtering headers only...");
if (responseEntity instanceof ResponseEntity
&& outputMessage instanceof ServerHttpResponse)
{
((ServerHttpResponse) outputMessage)
.setStatusCode(((ResponseEntity<?>) responseEntity)
.getStatusCode());
}
HttpHeaders entityHeaders = responseEntity.getHeaders();
if (!entityHeaders.isEmpty())
{
outputMessage.getHeaders().putAll(entityHeaders);
}
}
private HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest)
throws Exception
{
HttpServletRequest servletRequest = webRequest
.getNativeRequest(HttpServletRequest.class);
return new ServletServerHttpRequest(servletRequest);
}
private HttpOutputMessage createHttpOutputMessage(
NativeWebRequest webRequest) throws Exception
{
HttpServletResponse servletResponse = (HttpServletResponse) webRequest
.getNativeResponse();
return new ServletServerHttpResponse(servletResponse);
}
}
Then you will have to reference it within your spring context
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="theRealm" />
<property name="exceptionRenderer" ref="headerOnlyExceptionRender" />
</bean>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="theRealm/client" />
<property name="typeName" value="Basic" />
<property name="exceptionRenderer" ref="headerOnlyExceptionRender" />
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
<property name="exceptionRenderer" ref="headerOnlyExceptionRender" />
</bean>
<bean id="headerOnlyExceptionRender" class="org.some.nice.code.HeaderOnlyOAuth2ExceptionRenderer"/>
Hope it helps.

Database connection spring framework unresolved

i had earlier created the spring framework, then replaced with the database connection, but there is problem in creating the beans.
also receiving the below error during the deployment.
DEFAULT=C:\Users\gopc\Documents\NetBeansProjects\HelloSpringJDBC\build\web&name=HelloSpringJDBC&contextroot=/HelloSpringJDBC&force=true
failed on GlassFish Server 3.1.2 Error occurred during deployment:
Exception while loading the app : java.lang.IllegalStateException:
ContainerBase.addChild: start: org.apache.catalina.LifecycleException:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'productManager' defined in ServletContext
resource [/WEB-INF/applicationContext.xml]: Error setting property
values; nested exception is
org.springframework.beans.NotWritablePropertyException: Invalid
property 'productDao' of bean class [SimpleProductManager]: Bean
property 'productDao' is not writable or has an invalid setter method.
Does the parameter type of the setter match the return type of the
getter?. Please see server.log for more details.
C:\Users\gopc\Documents\NetBeansProjects\HelloSpringJDBC\nbproject\build-impl.xml:1029:
The module has not been deployed. See the server log for details.
Source
applicationContext.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: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-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="externalDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" scope="singleton" destroy-method="close">
<property name="driverClassName" value="sun.jdbc.odbc.JdbcOdbcDriver"/>
<property name="url" value="jdbc:odbc:;DRIVER=Microsoft Access Driver (*.mdb, *.accdb);DBQ=C://Users//gopc//Documents//odbc_sql.accdb"/>
<property name="username" value=""/>
<property name="password" value=""/>
</bean>
<bean id="productManager" class="SimpleProductManager">
<property name="productDao" ref="productDao"/>
</bean>
<bean id="productDao" class="JdbcProductDao">
<property name="dataSource" ref="externalDataSource"/>
</bean>
</beans>
spirngapp-servlet.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: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-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>
<bean name="/hello.htm" class="HelloController">
<property name="productManager" ref="productManager"/>
</bean>
<!-- we can prefix="/"
http://localhost:8080/HelloSpring/hello.htm
specify the path in modelview from the controller
OR
Decouple the view from the controller
prefix="/WEB-INF/jsp/"
-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en_US"/>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:viewClass="org.springframework.web.servlet.view.JstlView"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
</beans>
JdbcProductDao.java
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;
public class JdbcProductDao extends SimpleJdbcDaoSupport implements ProductDao {
protected final Log logger = LogFactory.getLog(getClass());
public List<Product> getProductList() {
logger.info("Getting products!");
List<Product> products = getSimpleJdbcTemplate().query(
"select id, description, price from products",
new ProductMapper());
return products;
}
public void saveProduct(Product prod) {
logger.info("Saving product: " + prod.getDescription());
int count = getSimpleJdbcTemplate().update(
"update products set description = :description, price = :price where id = :id",
new MapSqlParameterSource().addValue("description", prod.getDescription())
.addValue("price", prod.getPrice())
.addValue("id", prod.getId()));
logger.info("Rows affected: " + count);
}
private static class ProductMapper implements ParameterizedRowMapper<Product> {
public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
Product prod = new Product();
prod.setId(rs.getInt("id"));
prod.setDescription(rs.getString("description"));
prod.setPrice(new Double(rs.getDouble("price")));
return prod;
}
}
}
SimpleProductManager.java
import java.util.List;
import java.util.ArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class SimpleProductManager implements ProductManager {
protected final Log logger = LogFactory.getLog(getClass());
private ProductDao productDao;
public List<Product> getProductDao() {
//throw new UnsupportedOperationException();
return productDao.getProductList();
}
public void increasePrice(int percentage) {
//throw new UnsupportedOperationException();
List <Product> products = productDao.getProductList();
for (Product product : products) {
double newPrice = product.getPrice() * (100 + percentage)/100;
product.setPrice(newPrice);
productDao.saveProduct(product);
}
}
public void setProductDao(ProductDao productDao) {
//throw new UnsupportedOperationException();
logger.info("inside the setProducts in SimpleProductManager");
this.productDao = productDao;
}
}
HelloController.java
import java.util.Date;
import java.util.Map;
import java.util.HashMap;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
public class HelloController implements Controller {
private ProductManager productManager;
protected final Log logger = LogFactory.getLog(getClass());
/*
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
logger.info("Returning hello view");
String now = ( new Date().toString());
logger.info("time now:"+ now);
//return new ModelAndView("hello");
//return new ModelAndView("WEB-INF/jsp/hello","now",now);
//decouple the view from the controller
return new ModelAndView("hello","now",now);
}
*/
//Writing some business logic in the controller
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String now = (new java.util.Date()).toString();
logger.info("returning hello view with " + now);
Map<String, Object> myModel = new HashMap<String, Object>();
myModel.put("now", now);
myModel.put("products", this.productManager.getProductDao());
return new ModelAndView("hello", "model", myModel);
}
public void setProductManager(ProductManager productManager)
{
this.productManager = productManager;
}
}
hey the problem got resovled, after i had modified, the following..
1) applicationContext with mapping for the bean "productManager" with ref productDao.
2) ProductManager interface with new method call getProducts(), then implemenent in the SimpleProductManager and which call the ProductDao.getProducts(), where the sql query is being defined.

Categories

Resources