Sunday, May 11, 2014

how to build your web application on spring boot and deploy it on heroku

Why would you need a tutorial for building your web application on spring boot and deploying it on heroku?
That's because our web application will serve JSP files and as Spring Boot's JSP support is limited we will have to use war files for deployment and it can get a bit tricky to deploy it on heroku. Furthermore, as the web application we will deploy is super concise you can use it on your own project by just editing it.

Our controller is quite basic. It will take the name of the user from url and print it on the next page. So calling "/hello/sezin" will print on "hello sezin" on the page.


package main.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author sezin karli (skarligmail.com)
 * @since 3/19/14 3:42 PM
 *        User: Sezin Karli
 */
@Controller
@RequestMapping(value = "/hello")
public class HelloController {

    @RequestMapping(value = "/{user}")
    public String handleOne(@PathVariable String user, ModelMap modelMap){
        String helloToken = "Hello " + user;
        modelMap.put("token", helloToken);
        return "welcome-page";
    }
}

Our main Spring class is below. PORT attribute is taken from environment variables if it can't be found 8080 is used. This part is a requirement for Heroku.

package main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author sezin karli (skarligmail.com)
 * @since 3/19/14 9:26 AM
 *        User: Sezin Karli
 */
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class ToyProjectLauncher {

    public static void main(String[] args) throws Exception {
        String webPort = System.getenv("PORT");
        if (webPort == null || webPort.isEmpty()) {
            webPort = "8080";
        }
        System.setProperty("server.port", webPort);

        SpringApplication.run(ToyProjectLauncher.class, args);
    }
}

Our application properties. We need to show a folder for jsp files and if we don't want to print ".jsp" suffix for every return value in the controller we will also need the suffix parameter below.

server.port: ${port:8080}
spring.view.prefix: /WEB-INF/jsp/
spring.view.suffix: .jsp
Our jsp is as follows. We just print the token we get from the controller.
<%@ page language="java" contentType="text/html; charset=US-ASCII"
         pageEncoding="US-ASCII"%>



    
    Hello Page
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    






If we analyze the pom below you can see that we have a spring boot parent which's needed to inherit defaults from spring boot. Then we add starter-web dependency because it will add web dependency template. Jasper and jstl dependencies are needed for JSPs to work. We would need spring-boot-maven-plugin if we want a fat jar which makes it easier to deploy.

    4.0.0

    nr.co.caught
    boot-toy-project
    1.0-SNAPSHOT
    war


    
    
        org.springframework.boot
        spring-boot-starter-parent
        1.0.0.BUILD-SNAPSHOT
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
        
        
            javax.servlet
            jstl
        

    

    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

    
        
            spring-snapshots
            Spring Snapshots
            http://repo.spring.io/snapshot
            
                true
            
        
    

    
        
            spring-snapshots
            http://repo.spring.io/snapshot
        
    

You can check GitHub if you need further details with the project. If everyting's fine you should be able to build it with "mvn package".
You must see your war file under target folder. Why a war file? Because Spring Boot supports JSPs only if the project package is war.
 Lets run our code by going under "target" and doing "java -jar boot-toy-project-1.0-SNAPSHOT.war". You should see your hello page if you type "localhost/hello/sezin".

Now we will continue by deploying it on heroku. I assume you already have a heroku user and heroku toolbelt already installed.

Open a command prompt and login to heroku by typing "heroku login". Type your heroku email and password. It says "could not find an existing public key. We try to generate one but get a "could not generate key: 'ssh-keygen' is not recognized" error (under windows 7).


There seems to be a problem.
Open a git bash and lets create an ssh key with 'ssh-keygen -t rsa -C "yourEmail"' command.

Lets login again and here we get "authentication successful".



After that we will try to push and deploy our Spring Boot Project.
Create a file named "Procfile" with the information below under our project folder

 web: java $JAVA_OPTS -jar target/boot-toy-project-1.0-SNAPSHOT.war

There's another way to deploy war files to heroku (heroku deploy:war), but I didn't manage to run my application with it so I'm teaching you what worked for me. :)

Now, create a project with "heroku create" command. We will get "Git remote heroku added".


Open git bash and enter "git push heroku master" under your project directory. Now we will push our code into heroku git and heroku will deploy it to our server.



The last line will show you on which address your site is available. Mine was "http://pure-shelf-8719.herokuapp.com/". I add few strings to test my hello page and go to "http://pure-shelf-8719.herokuapp.com/hello/world". You can see the awesome result below.


Thursday, May 8, 2014

expression language injection attacks with the help of springJspExpressionSupport

Recently, I discovered a nasty place for expression language injection attack in one of my applications. This was directly related to the expression evaluation feature of spring components on jsp.
I was adding a request parameter to a spring form's action and as spring's form component directly evaluates it, you were able to put a ${applicationScope} (or anything on the page) on the request parameter and see the evaluation result on the source code.

First thing to do was setting springJspExpressionSupport to false by editing my web.xml and setting the flag to false.

 
        Enable Spring JSP Expressions
        springJspExpressionSupport
        false
    

The problem with this is the fact that it totally disables Spring's evaluation mechanism which means  ExpressionEvaluationUtils.evaluate() calls won't work anymore. So I had to use an alternative way such as the following code:

ELContext elContext = pageContext.getELContext();
JspApplicationContext jac = JspFactory.getDefaultFactory().getJspApplicationContext(pageContext.getServletContext());
 ValueExpression val = jac.getExpressionFactory().createValueExpression(elContext, exp, resultClass);
return val.getValue(elContext);

Tuesday, April 15, 2014

aboutme rocks

I'm quite lazy about creating a new homepage and aboutme comes to my rescue. It is easy to use and looks plain beautiful. Check my homepage.

Tuesday, April 8, 2014

mongodb default configuration in spring boot

I'm trying to integrate mongodb to my spring boot application. I create a db aptly named "mydb" and was wondering how mongodb classes were able to connect to my database without any configuration and why they were not retrieving my data. Answer to both is of course "default configuration" for mongodb.

You can see mongodb defaults that spring boot uses:

host: localhost
port: 27017
db: test

My mongodb is at localhost:27017, but as I'm using "mydb" instead of "test" no data is retrieved. So I have to override the default configuration by adding
spring.data.mongodb.uri=mongodb://localhost:27017/mydb
to my application.properties file.

Friday, March 21, 2014

problem with displaying jsp on spring boot

I'm currently trying to learn Spring Boot (1.0.0 snapshot) in my spare time. I try to use spring-boot-starter-web for building a sample website but had problems when I try to use jsp for the view part of my mvc. Everything was in place.

  • I had a jsp named "welcome-page.jsp" under "src/main/webapp/WEB-INF/jsp/" 
  • In my controller I was sending the user to "welcome-page". 
  • The path configuration was done under application.properties with
    • spring.view.prefix: /WEB-INF/jsp/
    • spring.view.suffix: .jsp


And yet it was not working. Nothing on logs too, even on debug level.
After some search and several trial/errors, I saw that it is impossible to use embedded tomcat for displaying jsps without adding several dependencies to my main pom. Next to the standard spring-boot-starter-web dependency, I had to add jasper and jstl dependencies as well. So long for leaving dependencies to starter templates.


   
 
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
            provided
        
        
            javax.servlet
            jstl