Render multiple views within a single request - json

I'm trying to return multiple views within a single request, returning them all in a JSON string.
Example:
#RequestMapping(value = "my-request")
public void myRequest(HttpServletRequest request, HttpServletResponse response) throws Exception
{
Map<String,Object> model1 = new Hashtable<String,Object>();
model1.put(...);
ModelAndView modelAndView1 = new ModelAndView("path/to/view1", model1);
// Render modelAndView1 in some way, in order to obtain the rendered HTML as a String
Map<String,Object> model2 = new Hashtable<String,Object>();
model2.put(...);
ModelAndView modelAndView2 = new ModelAndView("path/to/view2", model2);
// Render modelAndView2 in some way, in order to obtain the rendered HTML as a String
// Now write a JSON String to the HttpServletResponse containing both the rendered views (strings).
// (this is not part of my problem, I'm able to do it as long as I have the two strings)
}
I'm using Spring MVC with Tiles 2.
Can anyone help me?
Update 1 - View names are resolved using a ViewResolver:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/tiles/" />
<property name="suffix" value=".jsp" />
</bean>
Update 2 - I've created a github repository containing a very small example reproducing the problem.

UPDATE: There are some approaches for solving your issue. I'd try this one, but maybe there are cleaner ways.
#Component
public class JsonMultipleViewFactory {
#Autowired private List<ViewResolver> viewResolverList;
public View getView(List<ModelAndView> mavList) {
for (ModelAndView mav : mavList) {
if (mav.getView()==null) {
mav.setView(resolve(mav.getViewName()));
}
}
return new JsonMultipleView(mavList);
}
private View resolve(String viewName) {
for (ViewResolver vr : viewResolverList) {
View view = vr.resolve(viewName, LocaleContextHolder.getLocale());
if (view!=null) {
return view;
}
}
return null;
}
}
public class JsonMultipleView implements View {
private final List<ModelAndView> mavList;
public JsonMultipleView(List<ModelAndView> mavList) {
this.mavList = mavList;
}
public String getContentType() {
return "application/json";
}
public void render(Map<String,?> model, HttpServletRequest request, HttpServletResponse response) {
Json json = new Json(); // You can use several Json libraries here
for (ModelAndView mav : mavList) {
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
mav.getView().render(mav.getModel(), request, mockResponse);
json.add(mav.getViewName(), mockResponse.getContentAsString());
}
json.write(response.getOutputStream());
response.getOutputStream().close();
}
}
And can be used like this:
#Autowired private JsonMultipleViewFactory jsonMultipleViewFactory;
#RequestMapping(value = "my-request")
public View myRequest() {
...
List<ModelAndView> mavList = new ArrayList<ModelAndView>();
mavList.add(modelAndView1);
mavList.add(modelAndView2);
return jsonMultipleViewFactory.getView(mavList);
}

Related

Camel - CSV Headers setting not working

I have CSV files without headers. Since I'm using 'useMaps' I want to specify the headers dynamically. If I set headers statically and then use in route it works fine as below Approach 1 -
#Component
public class BulkActionRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
CsvDataFormat csv = new CsvDataFormat(",");
csv.setUseMaps(true);
ArrayList<String> list = new ArrayList<String>();
list.add("DeviceName");
list.add("Brand");
list.add("status");
list.add("type");
list.add("features_c");
list.add("battery_c");
list.add("colors");
csv.setHeader(list);
from("direct:bulkImport")
.convertBodyTo(String.class)
.unmarshal(csv)
.split(body()).streaming()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
GenericObjectModel model = null;
HashMap<String, String> csvRecord = (HashMap<String, String>)exchange.getIn().getBody();
}
});
}
}
However, if the list is passed via Camel headers as below then it does not work Approach 2 -
#Component
public class BulkActionRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
CsvDataFormat csv = new CsvDataFormat(",");
csv.setUseMaps(true);
from("direct:bulkImport")
.convertBodyTo(String.class)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
ArrayList<String> fileHeaders = (ArrayList<String>)headers.get(Constants.FILE_HEADER_LIST);
if (fileHeaders != null && fileHeaders.size() > 0) {
csv.setHeader(fileHeaders);
}
}
})
.unmarshal(csv)
.split(body()).streaming()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
GenericObjectModel model = null;
HashMap<String, String> csvRecord = (HashMap<String, String>)exchange.getIn().getBody();
}
});
}
}
What could be missing in the Approach 2?
The big difference between approach 1 and 2 is the scope.
In approach 1 you fully configure the CSV data format. This is all done when the Camel Context is created, since the data format is shared within the Camel Context. When messages are processed, it is the same config for all messages.
In approach 2 you just configure the basics globally. The header configuration is within the route and therefore can change for every single message. Every message would overwrite the header configuration of the context-global data format instance.
Without being sure about this, I guess that it is not possible to change a context-global DataFormat inside the routes.
What would you expect (just for example) when messages are processed in parallel? They would overwrite the header config against each other.
As an alternative, you could use a POJO where you can do your dynamic marshal / unmarshal from Java code.

spring boot: change print console to json rest

Actually, my project in Spring send values from the database via console, like this:
Console image, but I want to send this values via JSON like a rest API, but I don't know how to change that.
{
"depositarios": {
"correo": "correo",
"nombre": "nombre",
"numTel": "numTel",
"pApellido": "pApellido",
"SApellido": "sAellido"
}
}
this is my main class:
#SpringBootApplication
#ComponentScan("com.abner.springpostgresql.service.impl, com.abner.springpostgresql.dao.imp")
public class SpringPostgresqlApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(SpringPostgresqlApplication.class, args);
depoService depoService =context.getBean(depoService.class);
depoService.loadAllDepo();
}
}
this is my entired project source https://github.com/abnercoronado/restpostgresql
You have to create a RestController using the #RestController annotation like this:
#RestController
public class MyRestController {
#RequestMapping(value = "/personas", method = RequestMethod.GET)
public List<Persona> listaPersonas() {
// This is just a sample. Here you could bring your data form your db
List<Persona> lista = new ArrayList<Persona>();
Persona p = new Persona();
p.setNombre("angel");
p.setEdad(20);
lista.add(p);
return lista;
}
}
The value of the #RequestMapping annotation ("/personas" in this example) will be the endpoint. So when you access to the endpoint http://localhost:8080/personas (asuming that your app is running on http://localhost:8080) then you will get your data as json.
Here is an example of how to do it.
Here is another example (en espaƱol) that could help you.
You can use ObjectMapper to convert your pojo or object to JSON String and send where ever you wanted using thier API or anything.
Or you can create Rest Method and Access the API would return you the Json Value.
#RestController
public class MyRestController {
#RequestMapping(value = "/depo", method = RequestMethod.GET)
public List<?> getDepo() {
ApplicationContext context= SpringApplication.run(SpringPostgresqlApplication.class, args);
depoService depoService =context.getBean(depoService.class);
List<?> lista = depoService.loadAllDepo();
return lista;
}
Another way of doing.
#RestController
public class MyRestController {
#RequestMapping(value = "/depo", method = RequestMethod.GET)
public List<Depo> getDepo() {
ApplicationContext context= SpringApplication.run(SpringPostgresqlApplication.class, args);
depoService depoService =context.getBean(depoService.class);
List<Depo> lista = depoService.loadAllDepo();
return lista;
}
Once you start your server, you can run this by doing localhost:8080/depo. you can also return XML.

Add java List in JsonObject return value

I want to add a list in the json return value. Here is my code -
#RequestMapping(value = "/servicearea", produces="application/json")
#ResponseBody
public String ServiceArea(Model model, HttpServletRequest req, HttpServletResponse res)
{
List<ServiceArea> serviceLists = locationService.getAllServiceArea();
JsonObject result = Json.createObjectBuilder()
.add("message", "test")
.add("serviceLists", serviceLists)
.build();
return result.toString();
}
'serviceLists' is the one that I want to add. I'm getting error in this line - .add("serviceLists", serviceLists). Error message is that JsonObjectBuilder is not applicable for the arguments.
Thanks in advance.
Just create a pojo like this
class ReturnPojo {
private String message;
private List<ServiceArea> serviceLists;
//getters and setters
}
And from your controller you can do
ReturnPojo returnPojo = new ReturnPojo;
returnPojo.setMessage("test");
returnPojo.setServiceLists(serviceLists);
return returnPojo;
Change the method signature to return ReturnPojo
Below solution is only useful if you are using Rest API and Spring.
create class ServiceListPojo:
public class ServiceListPOJO{
List<ServiceArea> serviceLists;
public void setServiceLists(List<ServiceArea> serviceLists){
this.serviceLists = serviceLists;
}
}
Annotate your controller with #RestController
#RestController
public class SomeController{
#RequestMapping(value = "/servicearea")
public ServiceListPOJO ServiceArea()
{
List<ServiceArea> serviceLists = locationService.getAllServiceArea();
ServiceListPOJO slp = new ServiceListPOJO();
slp.setServciceLists(serviceLists);
return slp;
}
}

Add new line at the end of Jersey generated JSON

I have a Jersey (1.x) based REST service. It uses Jackson 2.4.4 to generate JSON responses. I need to add a newline character at the end of response (cURL users complain that there's no new line in responses). I am using Jersey pretty-print feature (SerializationFeature.INDENT_OUTPUT).
current: {\n "prop" : "value"\n}
wanted: {\n "prop" : "value"\n}\n
I tried using a custom serializer. I need to add \n only at the end of the root object. Serializer is defined per data type, which means, if an instance of such class is nested in a response, I will get \n in the middle of my JSON.
I thought of subclassing com.fasterxml.jackson.core.JsonGenerator.java, overriding close() where i'd add writeRaw('\n'), but that feels very hacky.
Another idea would be to add Servlet filter which would re-write the response from Jersey Filter, adding the \n and incrementing the contentLenght by 1. Seems not only hacky, but also inefficient.
I could also give up Jersey taking care of serializing the content and do ObjectMapper.writeValue() + "\n", but this is quite intrusive to my code (need to change many places).
What is the clean solution for that problem?
I have found these threads for the same problem, but none of them provides solution:
http://markmail.org/message/nj4aqheqobmt4o5c
http://jackson-users.ning.com/forum/topics/add-newline-after-object-serialization-in-jersey
Update
Finally I went for #arachnid's solution with NewlineAddingPrettyPrinter (also bumper Jackson version to 2.6.2). Sadly, it does not work out of the box with Jaskson as JAX-RS Json provider. Changed PrettyPrinter in ObjectMapper does not get propagated to JsonGenerator (see here why). To make it work, I had to add ResponseFilter which adds ObjectWriterModifier (now I can easily toggle between pretty-print and minimal, based on input param ):
#Provider
public class PrettyPrintFilter extends BaseResponseFilter {
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
ObjectWriterInjector.set(new PrettyPrintToggler(true));
return response;
}
final class PrettyPrintToggler extends ObjectWriterModifier {
private static final PrettyPrinter NO_PRETTY_PRINT = new MinimalPrettyPrinter();
private final boolean usePrettyPrint;
public PrettyPrintToggler(boolean usePrettyPrint) {
this.usePrettyPrint = usePrettyPrint;
}
#Override
public ObjectWriter modify(EndpointConfigBase<?> endpoint, MultivaluedMap<String, Object> responseHeaders,
Object valueToWrite, ObjectWriter w, JsonGenerator g) throws IOException {
if (usePrettyPrint) g.setPrettyPrinter(new NewlineAddingPrettyPrinter());
else g.setPrettyPrinter(NO_PRETTY_PRINT);
return w;
}
}
}
Actually, wrapping up (not subclassing) JsonGenerator isn't too bad:
public static final class NewlineAddingJsonFactory extends JsonFactory {
#Override
protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException {
return new NewlineAddingJsonGenerator(super._createGenerator(out, ctxt));
}
#Override
protected JsonGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException {
return new NewlineAddingJsonGenerator(super._createUTF8Generator(out, ctxt));
}
}
public static final class NewlineAddingJsonGenerator extends JsonGenerator {
private final JsonGenerator underlying;
private int depth = 0;
public NewlineAddingJsonGenerator(JsonGenerator underlying) {
this.underlying = underlying;
}
#Override
public void writeStartObject() throws IOException {
underlying.writeStartObject();
++depth;
}
#Override
public void writeEndObject() throws IOException {
underlying.writeEndObject();
if (--depth == 0) {
underlying.writeRaw('\n');
}
}
// ... and delegate all the other methods of JsonGenerator (CGLIB can hide this if you put in some time)
}
#Test
public void append_newline_after_end_of_json() throws Exception {
ObjectWriter writer = new ObjectMapper(new NewlineAddingJsonFactory()).writer();
assertThat(writer.writeValueAsString(ImmutableMap.of()), equalTo("{}\n"));
assertThat(writer.writeValueAsString(ImmutableMap.of("foo", "bar")), equalTo("{\"foo\":\"bar\"}\n"));
}
A servlet filter isn't necessarily too bad either, although recently the ServletOutputStream interface has been more involved to intercept properly.
I found doing this via PrettyPrinter problematic on earlier Jackson versions (such as your 2.4.4), in part because of the need to go through an ObjectWriter to configure it properly: only fixed in Jackson 2.6. For completeness, this is a working 2.5 solution:
#Test
public void append_newline_after_end_of_json() throws Exception {
// Jackson 2.6:
// ObjectMapper mapper = new ObjectMapper()
// .setDefaultPrettyPrinter(new NewlineAddingPrettyPrinter())
// .enable(SerializationFeature.INDENT_OUTPUT);
// ObjectWriter writer = mapper.writer();
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writer().with(new NewlineAddingPrettyPrinter());
assertThat(writer.writeValueAsString(ImmutableMap.of()), equalTo("{}\n"));
assertThat(writer.writeValueAsString(ImmutableMap.of("foo", "bar")),
equalTo("{\"foo\":\"bar\"}\n"));
}
public static final class NewlineAddingPrettyPrinter
extends MinimalPrettyPrinter
implements Instantiatable<PrettyPrinter> {
private int depth = 0;
#Override
public void writeStartObject(JsonGenerator jg) throws IOException, JsonGenerationException {
super.writeStartObject(jg);
++depth;
}
#Override
public void writeEndObject(JsonGenerator jg, int nrOfEntries) throws IOException, JsonGenerationException {
super.writeEndObject(jg, nrOfEntries);
if (--depth == 0) {
jg.writeRaw('\n');
}
}
#Override
public PrettyPrinter createInstance() {
return new NewlineAddingPrettyPrinter();
}
}
Not yet tested but the following should work:
public class MyObjectMapper extends ObjectMapper {
_defaultPrettyPrinter = com.fasterxml.jackson.core.util.MinimalPrettyPrinter("\n");
// AND/OR
#Override
protected PrettyPrinter _defaultPrettyPrinter() {
return new com.fasterxml.jackson.core.util.MinimalPrettyPrinter("\n");
}
}
public class JerseyConfiguration extends ResourceConfig {
...
MyObjectMapper mapper = new MyObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT); //enables pretty printing
// create JsonProvider to provide custom ObjectMapper
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
provider.setMapper(mapper);
register(provider); //register so that jersey use it
}
Do not know if this is the "cleanest" solution but it feels less hacky than the others.
Should produce something like
{\n "root" : "1"\n}\n{\n "root2" : "2"\n}
But it seems that does not work if there is only one root element.
Idea is from https://gist.github.com/deverton/7743979

How to return JSON object in resolveException method of HandlerExceptionResolver in Spring MVC?

While implementing a File Uploader controller in Spring MVC I stucked with one problem. My code snap is given below.
#Controller
public class FileUploader extends AbstractBaseController implements HandlerExceptionResolver
{
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
#ResponseBody
public JSONObject handleFileUpload(#RequestParam("file") MultipartFile file)
{
JSONObject returnObj = new JSONObject();
if (file.isEmpty())
{
returnObj.put("success", "false");
returnObj.put("message", "File is empty");
}
else
{
try
{
//my file upload logic goes here
}
catch (Exception e)
{
returnObj.put("success", "false");
returnObj.put("message", "File not uploaded.");
}
}
return returnObj;
}
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object obj, Exception exception)
{
ModelAndView model = new ModelAndView();
Map map = new HashMap();
if (exception instanceof MaxUploadSizeExceededException)
{
// I want to return JSONObject from here like given below.
/**
* { "message":"File size exceeded", "success":"false" }
* */
map.put("message", "File size exceeded");
map.put("success", "false");
model.addObject(map);
}
return model;
}
}
and my spring configuration look likes
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<property name="maxUploadSize" value="300000"/>
</bean>
now In my controller I want to return JSONObject instead of ModelAndView in resolveException method in my controller as given in code snap because I am developing some like REST method to upload file.
any ideas?
Thanks
If you use the Spring 3.2 above, I recommend this way.
At first, declare the ControllerAdvice.
#Controller
#ControllerAdvice
public class JAttachfileApi extends BaseApi
And make the Exception Handler to response JSON Object as following.
#ExceptionHandler(MaxUploadSizeExceededException.class)
public #ResponseBody Map<String,Object> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException ex)
{
Map<String,Object> result = getResult();
JFileUploadJsonResponse errorResult = new JFileUploadJsonResponse();
errorResult.setError("Maximum upload size of "+ex.getMaxUploadSize()+" bytes exceeded.");
List<JFileUploadJsonResponse> resultData = new ArrayList<JFileUploadJsonResponse>();
resultData.add(errorResult);
result.put("files", resultData);
return result;
}
You simply can annotate the method resolveException as #ExceptionHandler() and then you can have its signature like any other controller method. So placing #ResponseBody before the return type should work.
"Much like standard controller methods annotated with a #RequestMapping annotation, the method arguments and return values of #ExceptionHandler methods can be flexible. For example, the HttpServletRequest can be accessed in Servlet environments and the PortletRequest in Portlet environments. The return type can be a String, which is interpreted as a view name, a ModelAndView object, a ResponseEntity, or you can also add the #ResponseBody to have the method return value converted with message converters and written to the response stream."