Overriding static file routes in Spark depending on the Accept header - java

I'm using Spark's (http://sparkjava.com/) static file routing, set up via:
externalStaticFileLocation('.../public');
for serving, amongst others, the index.html page placed in the public directory. So effectively, when you hit the server's URL, you get your index.html back. So far so good...
However, I would like to override this behavior when the request contains a specific Accept header, e.g., application/rdf+xml (basically different from the default text/html). In that case I would like to return some specific data rather than the index.html page.
Is there a simple way of achieving this in Spark? I couldn't find any solution to this in the documentation... Thanks for any tips!

Due to the change in the behavior you'd like to make, the content you want to serve now is no longer static, but dynamic.
You'll need to remove index.html from the static content directory and move it to resources directory (typically src/main/resources/templates).
Then, you'll need to create a route for the root of your domain name (/) and using a template engine serve the content.
When using a template engine you usually inject the dynamic data into a static template. Typically you do it using some sort of map.
In the case of text/html your map will be empty, and then it's like before -
serving a static page. If it's application/rdf+xml you'll fill the map with the relevant data and then serve the resulting dynamic page.
Example:
get("/", (request, response) -> {
if (isTextHtml(request)) {
// serve the index.html template as is (empty map, not null though)
// In this case index.html functinos as a static page
} else {
// use a map to have all relevant data for the index.html template
// In this case index.html functinos as a "real" template
}
});

Related

Exclude CSS, JS and IMG file from #WebServlet urlPattern Java

My Servlet show product details by slug
#WebServlet("/*")
public class ProductDetails extends HttpServlet {
for example :
mywebsite.com/product-name
But they can't load css, js and img files because it detect the pathInfo for a slug.
How can I exclude them to not be loaded in the servlet?
Unfortunately the Servlet spec does not define exclusion rules for URL pattern mappings. Also, matched paths take precedence over matched suffixes (see Servlet 4 spec, paragraphs 12.1 & 12.2). The mapping on this Servlet will match anything in the application's context path, including static resources.
What can you do:
You can split the logic to handle static resources in that servlet. I would not go for it, it will probably complicate the code and hurt the separation of concerns.
If your static resources are inside a specific directory and no slug will match it (e.g. all JS, CSS, HTML, image files under assets/), you can have another Servlet that serves the static content under this directory (e.g. #WebServlet("/assets/*")). The longest path mapping that matches the request takes precedence. You still need to write code for sending a file back to the client; this code is fairly easy, you get a chance to customize the headers of the static files (e.g. caching), and, in comparison to the previous solution, the code that handles static files lies in it's own Servlet, so nice separation of concerns.
If you do not want to bother with serving static files yourself (I wouldn't blame you), you can move the logic from your Servlet to another class. Then introduce a Servlet filter that will first check if the request is a GET for a static resource and, if so, simply calls filterChain.doFilter(request,response) and lets the application server handle the static file. If not, it calls your logic and sends the response that your Servlet would have sent.
Finally, if you make your Servlet the default Servlet by #WebServlet("/") (not "/*"). It will pick up any request that other Servlets do not. The application server should have a Servlet installed for static content that will serve any existing CSS, JS, etc files from your application. Anything else will end up in your default Servlet.
If you could find a way to separate static content from dynamic content, e.g. all static content under assets/, all dynamic content under the virtual path content/ it would be much easier to change the mapping of your Servlet to /content/* and be done with it.

Java Spring: stored renedered HTML/JSP

I have a spring applicalation that illustate a list of reports.
Everything works fine. Now i want to call a controller that "generates" all these reports and store them locally. That means all jsp-file should be 100% equally renedered like a browser-response but not returned to the browser but stored locally as an html file.
My idea is:
user call a url "generate all reports"
the suiteable controller render all reports in the same why the would call every report individually
the controller would store each rendered jsp to defined place as an html
everything will be zipped (works already) and the user get the zip-file with all reports as return
So the concrete question is:
How can i render a jsp file and store this "rendered return" locally as HTML
Thanks a lot for your respone and help
So final question is:
You want to set up controller and call that controller to save rendered result in local store without returning to the browser.
In this case you can #Autowire the controller class and call controller function. When controller is called in another method response will be returned to that calling method not to the browser. You can process returned multipart (html, jsp...) in calling method.

Custom path resolving static resources Spring

I am working on Spring Boot application. The general problem is the following: I've created REST API, a few controllers. However, I also have some static HTML files, located in "resources/static".
What I want to achieve, is to configure Spring resolvers so that I could access static content without appending ".html". On practise, I expect to access static HTML by path "ip:port/htmlPage" instead of "ip:port/htmlPage.html"
However, I don't want to create methods like this one:
#Controller
public class ViewMaster {
#RequestMapping("/home")
public String home() {
return "home";
}
So, properties like
spring.mvc.view.suffix=.html
not working for me. Any possibilities to avoid creation per page endpoint in a controller?
After reading your question i have tried a lot but unable to serve html from static folder without extention. What works for me is to create an #RequestMapping like this:
#RequestMapping(value="/static/{htmlName}")
String getStaticHtml(#PathVariable String htmlName){
return htmlName;
}
And moved html files to templates folder. So there is no need to create different end points to access html pages, just pass the name of html without extention and this will do the trick

How to understand server/client data transfer in Angular, when coming from JSP?

I have a good basic knowledge of how to create web applications using java and jsp, together with Expression Language and JSTL. I am trying to learn how to use Angular.js for my front end.
I've gone through several tutorials, and I am starting to get a fair grip of the basics. But I have yet to figure out how to transfer data from the server, to the front end. Most tutorials I've found, describe how to send data from the front end, to the server.
I know that a RESTful api back end is recommended for Angular web apps. Unfortunately, I have no experience with this, and I find it hard to learn both angular and RESTful at the same time. If possible, I would love to make a work around, so that I can use my existing server solution, and learn one element at the time.
Server side setup
Now, in my old setup, using javax.servlet.http.HttpServlet, i call the service(HttpServletRequest, HttpServletResponse)-method. Inside the method, i add attributes to the request, like this:
request.setAttribute("attribute1", "1");
request.setAttribute("attribute2", "2");
request.setAttribute("attribute3", "3");
Then, because of a front controller pattern, I pass these request variables on to a redirect like this:
request.getRequestDispatcher("/WEB-INF/" + myPageVar + ".jsp").
forward(request, response);
For now, I would ideally like to keep this server side setup.
Current client side data access:
In the current setup, I can now access the initiated variables in two different ways. Either in a javascript script, like below (does not work with objects, only simpler attributes like strings (including JSON)). the next lines of code is picked from a jsp-page that the servlet would have redirected to.
var attribute1 = ${requestScope.attribute1};
or in the html, like this (would work with objects):
<c:set var="attribute1" value="${requestScope.attribute1}"></c:set>
I guess that I could incorporate Expression Language, and use javascript variables to initialize variables in my angular modules, directives and controllers, but I would prefer to do it purely in Angular.
Are these attributes accessible in any way, using angular? I've been trying to read up on $http, and $scope, but there is a jungle of non-relevant info on those, which I haven't been able to navigate through yet.
If the data you want to make accessible to angular should be ready when user lands on the page, it could make sense to put data in javascript variable in jsp page as you suggest.
Since your var is in global scope you can get in your angular controller like this:
$scope.att1 = attribute1;
However if you want to update your data without re-rendering the whole page (and that is what you want pretty soon) you should use $http to call a servlet that returns json. You can relatively simply make this servlet without jax-rs by overriding doGet in httpServlet and use a lightweight json lib (like gson). This example will do it:
//Set up pojo and make it into json string:
SomeClass pojo =new SomeClass();
pojo.setX("this is X");
JSONObject jsonObject = new Gson().toJson(pojo);
String jsonStr=jsonObject.toJSONString();
//Modify response and write json string
httpServletResponse.setStatus(200);//We are ok
httpServletResponse.setContentType("application/json");
httpServletResponse.setCharacterEncoding("UTF-8");
Writer writer = httpServletResponse.getWriter();
writer.write(jsonStr);
writer.close();
//Thats it
In a simple setup you handle this response in your controller like this:
$http({method: 'GET', url: 'http://yourservleturl' })
.success(function(jsonStringFromServlet){
$scope.newData = jsonStringFromServlet;
})
.error(function(){
$scope.error = true;
});
In page-html you access the data with
<div>This is your new x: {{newData.x}}</div>
Don't use jsps at all, don't set attributes.
Only use static html, thats the benefit ! You can write angular directives that perform the funcationality of jsp includes, and have much cleaner code (no embedded jstl, java. just pure html).
Create server side code that returns json. Then your angular js code calls the rest api and populates the client side model.
If you have something that needs populating on startup, use javascript appropriately.
Here is a typical java method that returns json, using Jersey (similar to Spring MVC, resteasy, restlets, spark, apache cxf etc etc) :
#PATH("/myPojo")
#GET
public Response getPojo(Long id) {
Pojo pojo = myService.getPojo(id)
return Response.ok()
.entity(pojo)
.build();
}
In angualr you can then create, for example, $myPojoService.getPojo() that is injected to relevant controllers and calls this endpoint. When called it probably returns the pojo as json and then probably populates the $scope.model.pojo json object. Then the two way databinding of angular updates your gui ... and Boom, you are a full stack engineer/ front end dev!
There are a lot of different ways to accomplish this. This is just what I ended up using.
To expose values in your JSP to Angular you'll need to write them out in script tags and build up Javascript vars with them. Then you can access them from Angular. I'm doing this to pass-in authenticated user account information from server side to my angular code. Your JSP would contain code such as:
<script>
window.CURRENT_USER = {
id: <%=currentUser.getId()%>,
name: "<%=currentUser.getName()%>",
email: "<%=currentUser.getEmail()%>",
prevLogin: new Date(<%=currentUser.getPrevLoginAt().getTime()%>),
prevLoginIp: "<%=currentUser.getPrevLoginIp()%>"
};
</script>
Then in your angular controllers, you can access it like this:
var currentUser = $window.CURRENT_USER;
A better approach (mentioned by Jacob Nicolaisen) would be to use the Google GSon library to actually generate the JSON objects instead of hand coding them.)

Serve images outside web application

I want to access static files, which are outside my web application in a known directory. I have read many options over the www, but I have still some questions about this.
Basically I want to declare a context for the defaultservlet of my application server. In my case I'm trying with the Tapestry Tutorial, which is a Maven based project and imported to eclipse.
The idea was to create a httpservlet, which gets the file from the location. Do someone of you know where I can grab an example of such a servlet and how I can call him? I know that the servlet must be probably declared as a service, because all pages of the application need to access the files, but I could also be mistaken and it is enough to import it, let say, in the layout page (All pages use the layout.tml file). I basically don't have any clue how to do it with a servlet. Can someone show me the light?
Tank you very much.
Another simpler solution is to create a page which returns a stream response
public class StaticFile {
StreamResponse onActivate(String fileName) {
return new StaticFileStreamResponse(fileName);
}
}
Then in another component / page
#Inject ComponentResources resources;
public Link getStaticFileLink() {
return resources.createPageRenderLinkWithContext("StaticFile", "path/to/myFile.jpg");
}
TML
<img src="${StaticFileLink}" />
But then you won't take advantage of tapestry's 304 NOT_MODIFIED response as in my other solution.
The tapestry way of doing this is by contributing an AssetRequestHandler and an AssetFactory.
AppModule.java
public static void contributeAssetDispatcher(
MappedConfiguration<String, AssetRequestHandler> config,
ResourceStreamer streamer)
{
config.add("staticfile", new StaticFileAssetRequestHandler(streamer));
}
public void contributeAssetSource(
MappedConfiguration<String, AssetFactory> config)
{
config.add("staticfile", new StaticFileAssetFactory());
}
Then in your tml you can use
<img src="${asset:staticfile:path/to/myFile.jpg}" />
Take a look at the ContextAssetRequestHandler, ClasspathAssetRequestHandler, ContextAssetFactory and ClasspathAssetFactory for inspiration.
Be careful not to open up a security hole where a hacker can access any file on your server by passing file paths prefixed with ../../

Categories