Spring boot Request Mapping with Schedule - java

I'm newbie on spring boot. I'm already implement some request mappings with successfully output in json.
localhost:8080/gJson
{
ad: "Windows 10",
mimari: "amd64",
versiyon: "10.0",
diskSize: 918,
freediskSize: 614,
cores: 8,
usablediskSize: 614
}
And My Controller here
#EnableAutoConfiguration
#Controller
public class GreetingController {
#RequestMapping(value = "/gJson", produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody MyPojo gJson(){
...
}
}
And now, I need ... example when I'm going to this link > localhost:8080/GetInfo getting json from localhost:8080/gJson but every "X" seconds.
Thanks For Helping.

How is /GetInfo being served? Is it just a standard HTML page? If so you can code a Javascript element that has a setInterval() to make an XMLHttpRequest to the /gJson endpoint. There are a number of other ways to do it depending on what libraries you want to use to have browser to server communications.
* Update *
Sample project: https://github.com/ShawnTuatara/stackoverflow-38890600
Main aspect that allows the refresh is the HTML page at src/main/resources/static/GetInfo.html
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>GetInfo</title>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
</body>
<script type="text/javascript">
$(function() {
window.setInterval(function() {
$.ajax({
url : "/gJson"
}).done(function(data, status, jqXHR) {
$("body").text(jqXHR.responseText);
});
}, 10000);
});
</script>
</html>
The controller is straightforward as outlined in the question.
#EnableAutoConfiguration
#RestController
public class GreetingController {
#GetMapping(value = "/gJson", produces = MediaType.APPLICATION_JSON_VALUE)
public MyPojo gJson() {
return new MyPojo("Windows 10", System.currentTimeMillis());
}
}
Finally the MyPojo is just a simple two field class.
public class MyPojo {
private String ad;
private long timestamp;
public MyPojo(String ad, long timestamp) {
this.ad = ad;
this.timestamp = timestamp;
}
public String getAd() {
return ad;
}
public void setAd(String ad) {
this.ad = ad;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
I included the timestamp so that you can see the time refreshing every 10 seconds on the web page.

Related

getting error on springboot while upload list of multipartfile

I am trying to upload multiple files and I am facing an issue while doing so. Can someone please suggest what is wrong?
I am enclosing relevant snippets of my code to debug better.
html code
<label>
welcome {{name}}, welcome to new app.
</label>
<div>
<input type="file" multiple placeholder="Select Files to be upload" accept=".xlsx" (change)=selectedfiles($event)>
</div>
upload logic
selectedfiles(event){
this.selectedxlfiles=event.target.files;
this.fileandinstancekeyobj.filetoupload=this.selectedxlfiles;
this.fileandinstancekeyobj.instancekey=this.instancekey;
this.uploadservice.uploadtoserver(this.fileandinstancekeyobj).subscribe(result=>{
console.log(result);
})
}
uploadservice
uploadtoserver(selectedfileandinstacekeyobj): Observable<HttpEvent<{}>>{
let url:string=environment.url+'uploadfile';
const newrequest=new HttpRequest('POST',url,selectedfileandinstacekeyobj,{
reportProgress:true,
responseType:'text'
});
return this.http.request(newrequest);
}
springboot controller
#RestController
public class uploadcontroller {
#PostMapping("/uploadfile")
public ResponseEntity<String> handleupload(#RequestBody uploaddto dto){
System.out.println("sucessfull");
System.out.println(dto.getInstancekey()+" "+dto.getFiletoupload().length);
return ResponseEntity.status(HttpStatus.OK).body("ok");
}
upload DTO
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.springframework.web.multipart.MultipartFile;
class uploaddto {
List<MultipartFile> filetoupload;
String instancekey;
public uploaddto(List<MultipartFile> filetoupload, String instancekey) {
super();
filetoupload=new ArrayList<MultipartFile>();
this.filetoupload = filetoupload;
this.instancekey = instancekey;
}
public List<MultipartFile> getFiletoupload() {
return filetoupload;
}
public void setFiletoupload(List<MultipartFile> filetoupload) {
this.filetoupload = filetoupload;
}
public String getInstancekey() {
return instancekey;
}
public void setInstancekey(String instancekey) {
this.instancekey = instancekey;
}
}
I am receiving the following error
[org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Cannot deserialize instance of `java.util.ArrayList<org.springframework.web.multipart.MultipartFile>` out of START_OBJECT token;
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot deserialize instance of `java.util.ArrayList<org.springframework.web.multipart.MultipartFile>` out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 17] (through reference chain: com.example.demo.uploaddto["filetoupload"])]
Any suggestion would be appreciated
I am adding this answer so that i could help someone save his day, what i did
change in uploadcontroller
this.selectedxlfiles=event.target.files;
const data:FormData=new FormData();
for(let i=0;i<this.selectedxlfiles.length;i++){
this.currentfile=this.selectedxlfiles[i];
data.append('selectedfile',this.currentfile);
}
data.append('instancekey',this.instancekey);
this.uploadservice.uploadtoserver(data).subscribe(Response=>{
console.log(Response);
})
changes in upload service
uploadtoserver(data:FormData): Observable<HttpEvent<{}>>{
let url:string=environment.url+'uploadfile';
// console.log(url);
// const data: FormData=new FormData();
// data.append('selectedfile',selectedfile);
// data.append('instancekey',instancekey);
const newrequest=new HttpRequest('POST',url,data,{
reportProgress: true,
responseType: 'text',
});
return this.http.request(newrequest);
//return this.http.post(url,selectedfiles);
}
changes in springboot controller
#RestController
public class uploadcontroller {
#PostMapping("/uploadfile")
public ResponseEntity<String> handleupload(#ModelAttribute uploaddto dto){
System.out.println("sucessfull");
System.out.println(dto.getInstancekey()+" "+dto.getFiletoupload().length);
return ResponseEntity.status(HttpStatus.OK).body("ok");
}
the only change in controller #modelattribute

Spring/Thymeleaf Unit test: test does not send value for model correctly

I am writing unit tests for my code. Now I want to test if a value put into the form is correctly saved in the variable in the Controller. Both tests that depend on this model attribute being correct, fail. Because the model exists but stays null, this must mean I'm sending the value from my test the wrong way. How can I have my test include an entered value to test the post method correctly?
The test testPostValueInModel() fails with an AssertionError:
java.lang.AssertionError: Model attribute 'chosenTemp' does not exist
I must note that I'm pretty new to all this, so if anyone has an answer please provide some more code examples and explain what is going wrong so I can learn from my mistakes. Thank you.
Here's my test class:
#RunWith(SpringRunner.class)
#WebMvcTest(InvoerschermController.class)
#AutoConfigureMockMvc
public class InvoerschermTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testCorrectModel() {
try {
this.mockMvc.perform(get("/invoer", "20")).andExpect(status().isOk())
.andExpect(model().attributeExists("chosenTemp"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Test
public void testPost() {
try {
this.mockMvc.perform(post("/invoer", "20")).andExpect(status().isOk())
.andExpect(view().name("invoerscherm"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Test
public void testPostValueInModel() {
try {
this.mockMvc.perform(post("/invoer", "20")).andExpect(status().isOk())
.andExpect(model().attributeExists("chosenTemp"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
The Controller:
#Controller
public class InvoerschermController {
private String chosenTemp = "20";
private static PostgresDatabase database;
private static Connection connection;
// Static initializer for the database
static {
database = new PostgresDatabase();
connection = database.connectToDatabase();
}
#GetMapping("/invoer")
public String invoer(Model model) {
// int newTemp = Integer.parseInt(getChosenTemp());
chosenTemp = database.getTemperature(connection);
model.addAttribute("chosenTemp", getChosenTemp());
return "invoerscherm";
}
#PostMapping("/invoer")
public String addInputTemp(String chosenTemp, Model model) {
setChosenTemp(chosenTemp);
model.addAttribute("chosenTemp", getChosenTemp());
try {
int newTemp = Integer.parseInt(getChosenTemp());
database.setTemperature(connection, newTemp);
} catch (NumberFormatException nfe) {
System.err.println("Invalid number: " + nfe.getMessage());
}
return "invoerscherm";
}
public String getChosenTemp() {
return chosenTemp;
}
public void setChosenTemp(String chosenTemp) {
this.chosenTemp = chosenTemp;
}
}
The Thymeleaf:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:include="fragments/template :: head"></head>
<head>
<title>Smart CV</title>
</head>
<body>
<nav th:replace="fragments/template :: header"></nav>
<div class="container">
<div class="hero-unit">
<h1>Temperatuur instellen</h1>
</div>
<form action="#" th:action="#{/invoer}" th:object="${invoerscherm}"
method="post">
<div class="form-group">
<label for="chosenTemp">Gewenste temperatuur:</label> <input
type="text" class="form-control" id="chosenTemp" name="chosenTemp"
autocomplete="off" th:value="${chosenTemp}" />
</div>
<button type="submit" class="btn btn-default" name="submitKnop">Stel
in</button>
</form>
</div>
<nav th:replace="fragments/template :: footer"></nav>
</body>
</html>
First of all your controller is flawed. You shouldn't keep local state (try to imagine what happens to the chosenTemp field when 3 users submit at the same time as there is only a single instance of the InvoerschermController.
Your method argument should be annotated with #RequestParam("chosenTemp") to match the form you are sending. Your test should also reflect the fact that you are sending a parameter named chosenTemp.
First your controller
#Controller
public class InvoerschermController {
private static PostgresDatabase database;
private static Connection connection;
// Static initializer for the database
static {
database = new PostgresDatabase();
connection = database.connectToDatabase();
}
#GetMapping("/invoer")
public String invoer(Model model) {
Integer chosenTemp = database.getTemperature(connection);
model.addAttribute("chosenTemp", chosenTemp);
return "invoerscherm";
}
#PostMapping("/invoer")
public String addInputTemp(#RequestParam("chosenTemp") Integer chosenTemp, Model model) {
model.addAttribute("chosenTemp", chosenTemp);
database.setTemperature(connection, chosenTemp);
return "invoerscherm";
}
}
Notice the type change from String to Integer Spring will do the type conversion for you and notice the addition of the #RequestParam. Now your test should also reflect this.
#RunWith(SpringRunner.class)
#WebMvcTest(InvoerschermController.class)
#AutoConfigureMockMvc
public class InvoerschermTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testCorrectModel() {
try {
this.mockMvc.perform(get("/invoer")).andExpect(status().isOk())
.andExpect(model().attributeExists("chosenTemp"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Test
public void testPost() {
try {
this.mockMvc.perform(post("/invoer").param("chosenTemp", "20").andExpect(status().isOk())
.andExpect(view().name("invoerscherm"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Test
public void testPostValueInModel() {
try {
this.mockMvc.perform(post("/invoer").param("chosenTemp", "20")).andExpect(status().isOk())
.andExpect(model().attributeExists("chosenTemp"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Notice the addition of .param("chosenTemp", "20") to add a request parameter with that name.
Your controller is still flawed imho as it shouldn't care about the Connection that should all be encapsulated in your Database class. Although your test now probably works your actual application will still fail due to the use of Thymeleaf and form binding. The form binding expects an object under the key invoerScherm to be available and that object should have a property named chosenTemp. You are actually lacking a form object. So what your controller should actually look like.
First you need a form object:
public class InvoerScherm {
private Integer chosenTemp;
public InvoerScherm() {}
public InvoerScherm(Integer temp) { this.chosenTemp=temp;}
// Here be getters/setters
}
Then let your controller create and use it
#Controller
public class InvoerschermController {
private static PostgresDatabase database;
private static Connection connection;
// Static initializer for the database
static {
database = new PostgresDatabase();
connection = database.connectToDatabase();
}
#GetMapping("/invoer")
public String invoer(Model model) {
Integer chosenTemp = database.getTemperature(connection);
InvoerScherm invoerScherm = new InvoerScherm(chosenTemp);
model.addAttribute("invoerScherm", invoerScherm);
return "invoerscherm";
}
#PostMapping("/invoer")
public String addInputTemp(#ModelAttribute InvoerScherm invoerScherm, Model model) {
database.setTemperature(connection, invoerScherm.getChosenTemp());
return "invoerscherm";
}
}
Ofcourse now your test will fail again, but I leave that task to you.

minify html and js in jersey Interceptor

used jersey mvc and jsp, all requests to html or js files did through #Template or Viewable.
example;
#GET
#Path(JS_URL + "{type}")
#Template(name = "grid")
#Produces("application/javascript")
public Response buildJSGrid(#DefaultValue("") #PathParam("type") String type) {
Grid grid = new Grid(type);
....
return Response.ok(grid).build();
}
where grid is grid.jsp file with pure javascript inside
<%# page contentType="application/javascript;charset=UTF-8" language="java" %>
.....
also possible other variant with html and js, example;
#GET
#Path(FORM_URL + "{type}")
#Template(name = "form")
#Produces(MediaType.TEXT_HTML)
public Response buildAccountForm(#DefaultValue("") #PathParam("type") String type) {
Form form = new Form(type);
....
return Response.ok(form).build();
}
where form is form.jsp with html and js inside <script>..</script>
<%# page contentType="text/html;charset=UTF-8" language="java" %>
...
i need to minify result js and html/js before send to client, i try to use https://code.google.com/archive/p/htmlcompressor/ lib, but there need to pass String to htmlCompressor.compress(input);
tried use WriterInterceptor
public class MinifyJsInterceptor implements WriterInterceptor {
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
final OutputStream outputStream = context.getOutputStream();
// here need to convert outputStream to InputStream and after to String ?
// result string to htmlCompressor.compress(resultString);
// after that convert result minify string back to resultOutputStream and set to context ?
context.setOutputStream(new GZIPOutputStream(resultOutputStream));
is it correct way ? and i can`t converts that outputstream to string
thanks
--update
answer to questions;
html + js mean that in some jsp are html markup and js code
<div id="form" style="width: 500px; display: none">
<div class="w2ui-page page-0">
<div class="w2ui-field">
</div>....
<script type="text/javascript">
var uiElement = (function () {
var config = {
onOpen: function (event) {
event.onComplete = function () {
$('#formContainer').w2render('form');
}
...
}());
</script>
on client that file requested by
$('#tempContainer').load('that file name - also dynamic', function (data, status, xhr) {
uiElement.init();
w2ui[layout].content(layout_main, w2ui[uiElement.name]);
});
And do you really return js-files in you resource methods?
some js and html + js files are dynamic build, example;
grid.jsp contains inside
<%# page contentType="application/javascript;charset=UTF-8" language="java" %>
var uiElement = (function () {
var config = {
grid: {
name: ${it.name},
listUrl:'${it.entityListUrl}',
formUrl:'${it.entityFormUrl}',
columns: ${it.columns},
records: ${it.records},
}}
there are ${it..} values from el expression and setting in resource method
#GET
#Path(JS_URL + "{type}")
#Template(name = "grid")
#Produces("application/javascript")
public Response buildJSGrid(#DefaultValue("") #PathParam("type") String type) {
Grid grid = new Grid(type);
....
return Response.ok(grid).build();
}}
and from client that js 'file' called by
$.getScript('dynamic js file name' - it is dynamic too).done(function (script, status, xhr) {
//console.log(xhr.responseText);
uiElement.init();
w2ui[layout].content(layout_main, w2ui[uiElement.name]);
});
also some html blocks build dynamic
{
<c:if test="${it.recid != 0}">
<div class="w2ui-field">
<label>active:</label>
<div>
<input name="active" type="checkbox"/>
</div>
</div>
</c:if>
}
-- update description,
grid builder;
one resource and one template for build any grid,
#GET
#Path(GRID + "{type}")
#Template(name = W2UI_VIEW_PREFIX + "grid/grid")
#Produces(MEDIA_TYPE_APPLICATION_JAVASCRIPT)
public Response buildGrid(#DefaultValue("") #PathParam("type") String type) {
for (W2UI ui : W2UI.values()) {
if (type.equals(ui.getName())) {
W2UIElement grid = ui.getUI();
return Response.ok(grid).build();
}
}
return Response.noContent().build();
}
also possible different templates(jsp files) through Viewable(template, model)
somewhere in menu builder for menu.jsp template
List<MenuItem> items..
MenuItem item1 = new MenuItem(W2UI.TASK_GRID, W2UIService.GRID);
items.add(item1);
where
W2UIService.GRID is string url for client js request and for server method resource #Path() anno.
and
public enum W2UI {
TASK_GRID("task_grid", "tasks", Type.SCRIPT){
#Override
public W2UIElement getUI() {
return new TaskGrid(getName());
}
},
.....
}
TaskGrid is filled model for grid.jsp template with js code, so easy to add any type of grid with different sets of data and buttons.
type of component(Type.SCRIPT) processing on the client by $.getScript(), Type.HTML by $('#tempContainer').load()
---update factory and providers;
#Provider
#Priority(200)
#HtmlMinify
public class HtmlMinifyInterceptor implements WriterInterceptor {
#Inject private HtmlCompressor compressor;
...
public class HtmlMinifierFactory implements Factory<HtmlCompressor> {
private HtmlCompressor compressor;
#Override
public HtmlCompressor provide() {
if (null == compressor) compressor = new HtmlCompressor();
ClosureJavaScriptCompressor jsCompressor = new ClosureJavaScriptCompressor();
jsCompressor.setCompilationLevel(CompilationLevel.SIMPLE_OPTIMIZATIONS);
..
#ApplicationPath("/")
public class MainRsConfig extends ResourceConfig {
public MainRsConfig() {
..
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(HtmlMinifierFactory.class).to(HtmlCompressor.class).in(Singleton.class);
}
});
..
You can use a custom implementation of a ByteArrayOutputStream as a wrapper to the OutputStream of the WriterInterceptorContext:
import com.googlecode.htmlcompressor.compressor.Compressor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class HtmlMinifyOutputStream extends ByteArrayOutputStream {
private OutputStream origOut;
private Compressor compressor;
public HtmlMinifyOutputStream(OutputStream origOut, Compressor compressor) {
this.origOut = origOut;
this.compressor = compressor;
}
public void close() throws IOException {
super.close();
String compressedBody = compressor.compress(new String(this.buf));
this.origOut.write(compressedBody.getBytes());
this.origOut.close();
}
}
The HtmlMinifyOutputStream can be used in the WriterInterceptor implementation. The HtmlCompressor instance is injected:
import com.googlecode.htmlcompressor.compressor.Compressor;
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import java.io.*;
#Provider
#HtmlMinify
public class MinifyHtmlInterceptor implements WriterInterceptor {
#Inject
private Compressor compressor;
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
final OutputStream outputStream = context.getOutputStream();
context.setOutputStream(new HtmlMinifyOutputStream(outputStream, compressor));
context.proceed();
}
}
#HtmlMinify is a NameBinding annotation, used to activate the MinifyHtmlInterceptor on specific resource methods. (see https://jersey.java.net/documentation/latest/filters-and-interceptors.html#d0e9988):
import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#NameBinding
#Retention(value = RetentionPolicy.RUNTIME)
public #interface HtmlMinify {}
The HtmlCompressor can be created only once per application and used concurrently, because:
HtmlCompressor and XmlCompressor classes are considered thread safe* and can be used in multi-thread environment (https://code.google.com/archive/p/htmlcompressor/)
Here is a HK2 factory (see: Implementing Custom Injection Provider) which creates the compressor instance and enables inline css and javascript compression:
import com.googlecode.htmlcompressor.compressor.Compressor;
import com.googlecode.htmlcompressor.compressor.HtmlCompressor;
import org.glassfish.hk2.api.Factory;
public class HtmlCompressorFactory implements Factory<Compressor> {
private HtmlCompressor compressor;
#Override
public Compressor provide() {
if(compressor == null) {
compressor = new HtmlCompressor();
}
compressor.setCompressJavaScript(true);
compressor.setCompressCss(true);
return compressor;
}
#Override
public void dispose(Compressor compressor) {}
}
The factory is registered with an AbstractBinder:
final ResourceConfig rc = new ResourceConfig().packages("com.example");
rc.register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(HtmlCompressorFactory.class).to(Compressor.class).in(Singleton.class);
}
});
If inline javascript or inline css compression is enabled:
HTML compressor with default settings doesn't require any dependencies. Inline CSS compression requires YUI compressor library.Inline JavaScript compression requires either YUI compressor library (by default) or Google Closure Compiler library. (https://code.google.com/archive/p/htmlcompressor/)
I use maven, so I added this dependency to my pom.xml:
<dependency>
<groupId>com.yahoo.platform.yui</groupId>
<artifactId>yuicompressor</artifactId>
<version>2.4.8</version>
</dependency>
If you want to use the Google Closure Compiler use this dependency:
<dependency>
<groupId>com.google.javascript</groupId>
<artifactId>closure-compiler</artifactId>
<version>r2388</version>
</dependency>
and activate it:
compressor.setJavaScriptCompressor(new ClosureJavaScriptCompressor());
compressor.setCompressJavaScript(true);
compressor.setCssCompressor(new YuiCssCompressor());
compressor.setCompressCss(true);
return compressor;
If you want to compress pure JavaScript or CSS files, you cannot use the htmlcompressor. This library supports only HTML files with inline CSS/JS. But you could implement a MinifyJsInterceptor or MinifyCssInterceptor analog to the MinifyHtmlInterceptor, which uses the YUI-Compressor and/or Google Closure libraries directly.
For gzip compression you should implement another interceptor. So it is possible to configure the minification and compression separately. If you activate multiple interceptors, use javax.annotation.Priority to controll the order of execution. (see: https://jersey.java.net/documentation/latest/filters-and-interceptors.html#d0e9927)

Spring MVC output CSV to highcharts

I am trying to populate highcharts chart using a csv file that I will generate in java back end and send to the from end using spring mvc.
First my controller class which I am almost positive is the issue but I don't know how to correctly send the csv file:
#Controller
public class ChartController {
#RequestMapping(value = "/index", method = RequestMethod.GET)
public String indexHandler() {
return "index";
}
#RequestMapping(value = "/out", method = GET)
public String chartHandler() {
String fileName = "test.csv" //note: I have also tried moving this
//file to my WEB-INF location and it doesn't make a difference
InputParser input = new InputParser();
for (GenericDataObject gdo : input.getDataObjects()) {
CSVOutput.writeCSV(fileName,gdo);
}
return "index";
}
}
The csv file is successfully created as I intend so that's not an issue
here is my Java script for highcharts
<html>
<head>
<script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/data.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<some more high charts for font and color that I will leave out because its not currently being used>
</head>
<body>
<div id='container' style="width: 100%; height: 600px;"></div>
<script type="text/javascript">
$(document)ready.function() {
var groupId = [];
var date = [];
var val = [];
var options = {
chart: {
renderTo: 'container',
type: 'line'
},
title: {
text: 'test'
},
xAxis: {
title: {
text: 'group and date'
},
categories: [groupId, date]
},
yAxis: {
title: {
text: 'data'
}
},
series: [{
data: val
}]
};
$.get('http://localhost:8080/web-data-app/out', function(data)) {
alert("success");
var lines = data.split('\n')
$.each(lines, function(lineNo, line) {
var items = line.split(',');
groupId.push(items[1]);
date.push(items[2]);
val.push(items[4]);
});
var cahrt = new Highcharts.Chart(options);
});
});
</script>
</body>
</html>
As of right now I get the outline of highcharts in my container, as well as the success alert so i know that much is working. however no data is being displaying withing the chart.
You can use httpResponse and send your content there. Like this
#RequestMapping(value = "/out", method = GET)
public void chartHandler(HttpServletResponse httpResponse) {
String fileName = "test.csv" //note: I have also tried moving this
//file to my WEB-INF location and it doesn't make a difference
InputParser input = new InputParser();
for (GenericDataObject gdo : input.getDataObjects()) {
CSVOutput.writeCSV(fileName,gdo);
}
httpResponse.setContentType("text/csv");
//you can use the output stream below to pass your content
httpResponse.getOutputStream()
}

How To Implement a progress bar using Spring 3 MVC?

Can anyone teach me or direct to a working example to satisfy this requirement.
Scenario:
List item My Web App is using spring mvc.
One of the services it provides is that when the user clicks on a button a long running process will occur on the server. (Query database, write files, write logs, etc...) this process can take a few seconds or a few minutes.
*Problem***
How can I implement the service to update the client of its progress.
The service returns true or false if the process was successful.
Thanks for your replies. A code snippet or a complete tutorial will be most helpful.
Here is a possible solution to this progress bar problem:
task.jsp
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%#taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<script src="../js/jquery.min.js"></script>
<script>
$(document).ready(function () {
$.getJSON(window.location.href.concat('/status'), function(data) {
if (data === "created") {
} else {
// task is already being executed
refreshProgress();
}
});
});
var width = 0;
function getProgress() {
$.getJSON(window.location.href.concat('/progress'), function(percentage) {
$('#progressBar').css('width', percentage+'%');
document.getElementById("label").innerHTML = percentage * 1 + '%';
width = percentage;
});
}
function start() {
$.ajax({
type: "post",
data: $('#task').serialize(),
success: function(data) {
$('#progressBar').css('width', 100+'%');
document.getElementById("label").innerHTML = 100 * 1 + '%';
// do sth with the data after finished task
}
});
width = 0;
$('#progressBar').css('width', 0+'%');
document.getElementById("label").innerHTML = 0 * 1 + '%';
refreshProgress();
}
function refreshProgress() {
$("#btnStart").prop("disabled",true);
var id = setInterval(frame, 1000);
function frame() {
if (width >= 100) {
clearInterval(id);
$("#btnStart").prop("disabled",false);
} else {
getProgress();
}
}
}
</script>
</head>
<body>
<div class="container">
<h2 class="text-center">Progress Bar Example</h2>
<div class="progress">
<div id="progressBar" class="progress-bar" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" style="width:0%">
<div id="label">0%</div>
</div>
</div>
<form:form method="POST" commandName="task" cssClass="form-horizontal">
<fieldset>
<div class="form-group">
<label class="col-md-4 control-label" for="btnStart">Actions</label>
<div class="col-md-8">
<button id="btnStart" name="btnStart" class="btn btn-success">Start</button>
<button id="btnStop" name="btnStop" class="btn btn-danger">Stop</button>
</div>
</div>
</fieldset>
</form:form>
</div>
<script>
$('#task').submit(function () {
start();
return false;
});
</script>
</body>
</html>
TaskController.java
#Controller
#RequestMapping(value = "/task")
public class TaskController {
private Task task;
#RequestMapping("")
protected ModelAndView page() {
ModelAndView model = new ModelAndView(VIEW_DIR + "task");
if (this.task == null) {
this.task = new Task();
}
model.addObject("task", this.task);
return model;
}
#RequestMapping(value = "/status", method = GET)
public #ResponseBody
String getStatus() {
return task.getStatus();
}
#RequestMapping(value = "/progress", method = GET)
public #ResponseBody
int getProgress() {
return task.getProgress();
}
public ModelAndView form(#ModelAttribute Task task) {
this.task = task;
ModelAndView model = new ModelAndView(VIEW_DIR + "task");
task.execute();
model.addObject("task", this.task);
return model;
}
}
Task.java
public class Task {
private int total;
private int progress;
private String status;
public Task() {
this.status = "created";
// TODO get total here or pass via form
}
public void execute() {
status = "executing";
int i = 0;
while (i < total && status.equals("executing")) {
progress = (100 * (i + 1) / total);
i++;
}
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
this.progress = progress;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
There are a good number of ways to handle a scenario like this. One way is to model the work in terms of a "Process", which contains a "status", including a percentage completion.
If you imagine what this might look like on a website, clicking the button to start the process would submit a form that begins the process and assigns some sort of identity to the process, almost like if you were creating any other sort of object. It would then redirect you to a "process status" page.
The process status page would query for the status of the process and display it. It'd probably have a URL parameter for the process's ID. It would perhaps update itself using an AJAX call to return a progress percentage.
On the backend, you now need to solve a couple of problems: finding out the current status of process N, and updating the status of process N. You could accomplish this in a number of ways, including storing the progress in the database or having some sort of in-memory table of running jobs. You could also use some sort of heuristic to estimate a percent. For example, if it's a "register new user" job, maybe it's 20% done if the user's table has an email address, 40% done if the user avatar table has data in it for this user, etc. I don't recommend this as much.

Categories