No description, website, or topics provided.
Switch branches/tags
Nothing to show
Clone or download

README.md

Restfull Issue Tracking Application With Spring Boot and AngularJs

In this tutorial I'm going to create a Restful Service Application with spring-boot, spring-jpa, hsqldb, AngularJs.

Maven is the only requirement for this tutorial. You may also want to use an IDE like Eclipse. Currently I have Eclipse Neon for Java EE Developers

Outline





Section 1 : Creating a project using Spring Initializr

Let's first create our project structure using "SPRING INITIALIZR" from https://start.spring.io/

I am going to generate a Maven project with Spring Boot 1.4.0 (which are selected by default).

Second step is to write a group name for the project. Mine is com.ferdisonmezay and the artifact is issuetracker

So the package names of this project will start with com.ferdisonmezay.issuetracker

Then we need to select the dependencies: you can do this by typing the names on the 'Dependencies' text field, or you can also click on 'Switch to the full version' link and select all the dependencies you want to use.

For this application I will use :

  • Web

  • Jersey (JAX-RS)

  • JPA

  • HSQLDB

After you select these dependencies, click on Generate Project button. Your project files will be generated and downloaded.

Now extract the zip file you've downloaded and cd into extracted folder and run

mvn spring-boot:run

Our spring boot application should be started. Now if you open your web browser and go to http://localhost:8080/ you will see Whitelabel Error Page, which means our application runs, and we didn't map anything yet.

Now I'm making a commit to github, Commit Message : initial project, downloaded from spring.io





Section 2 : Creating Web UI

In spring boot web applications we can serve websites as well. We need to put our web UI related files into src/main/resources/static folder.

I will copy-paste css, javascript, image files to static folder. Then I'll create an index file to display website content.

Here is the folder structure after copy-paste.

static
├── css
│   ├── font-awesome.min.css #for icons etc.
│   └── main.css #for UI styling
├── fonts
│   ├── FontAwesome.otf
│   ├── fontawesome-webfont.eot
│   ├── fontawesome-webfont.svg
│   ├── fontawesome-webfont.ttf
│   ├── fontawesome-webfont.woff
│   └── fontawesome-webfont.woff2
├── images
│   ├── banner.jpg
│   ├── logo.png
│   └── logo_big.png
└── js
    ├── angular-route.js
    ├── angular.min.js
    ├── bootstrap.min.js
    ├── custom.js # website custom js functions for scroll animations etc.
    ├── jquery-1.12.4.min.js
    └── jquery.easing.min.js

now we need to create index.html file in static folder.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Ferdi Sönmezay | Rest Application</title>

    <script type="text/javascript" src="./js/angular.min.js"></script>
    <script type="text/javascript" src="./js/angular-route.js"></script>

    <link rel="stylesheet" href="./css/main.css">
    <link rel="stylesheet" href="./css/font-awesome.min.css">

    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
       <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
       <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
   <![endif]-->
</head>

  <body>
    <header class="site-header">
      <div class="wrapper">
        <nav class="navbar navbar-default navbar-fixed-top" id="mysiteNav">
          <div class="container">
            <div class="row">
              <div class="col-md-4 col-sm-4 col-xs-12 col-lg-4">
                <div class="navbar-header">
                  <button type="button" class="no-space navbar-toggle collapsed pull-right" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    <i class="ion-navicon collapsed pull-right"></i>
                  </button>

                  <a class="navbar-brand-logo" href="./">
                    <div class="row">
                      <div class="col-md-1 col-sm-1 col-xs-1 col-lg-1">
                        <div class="logo">
                        	<img src="./images/logo.png">
                        </div>
                      </div>
                      <div class="col-md-11 col-sm-11 col-xs-11 col-lg-11">
                        <div class="brand collapse navbar-collapse">
                        	FERDİ SÖNMEZAY
                        	<p>Restapp</p>
                        </div>
                      </div>
                    </div>
                  </a>
                </div>
              </div>
              <div class="col-md-8 col-sm-8 col-xs-12 col-lg-8">
                <div id="navbar" class="collapse navbar-collapse">
                  <ul class="nav navbar-nav pull-right">
                    <li><a class="page-scroll" href="#/home">Home</a></li>
                    <li><a class="page-scroll" href="#/developer">Developers</a></li>
                    <li><a class="page-scroll" href="#/bug">Bugs</a></li>
                    <li><a class="page-scroll" href="#/story">Stories</a></li>
                    <li><a class="page-scroll" href="#/assign">Assign</a></li>
                  </ul>
                </div>
              </div>
            </div>
          </div>
        </nav>
      </div>
    </header>

    <div class="page-banner no-spacing paralax" data-speed="2" style="background-image: url('images/banner.jpg');">
      <div class="bg-overlay no-spacing paralax">
        <div class="container header-container text-center">
            <h3>
                REST API for an issue tracker of a small team of developers
            </h3>
        </div>
      </div>
    </div>
    <div class="page-wrap" id="page-wrap">
      <div class="container">
        <div class="page-content">
          <div class="wrapper">
            Content will be displayed here
          </div>
        </div>
      </div>
    </div>
    <footer class="site-footer">
      <div class="container">
        <div class="row">
          <div class="col-md-8 col-sm-8 col-xs-12 col-lg-8">
            <div>
              <ul>
                <li><a class="page-scroll" href="#/home">Home</a></li>
                <li><a class="page-scroll" href="#/developer">Developers</a></li>
                <li><a class="page-scroll" href="#/bug">Bugs</a></li>
                <li><a class="page-scroll" href="#/story">Stories</a></li>
                <li><a class="page-scroll" href="#/assign">Assign</a></li>
              </ul>
            </div>
          </div>
        </div>
      </div>
    </footer>
  </body>
  <script type="text/javascript" src="./js/jquery-1.12.4.min.js"></script>
  <script type="text/javascript" src="./js/bootstrap.min.js"></script>
  <script type="text/javascript" src="./js/custom.js"></script>
</html>

Here is second commit for the initial web ui.

Now we need to create an AngularJs application file called 'restapp.js' in js folder.

//js/restapp.js

(function() {
    var app = angular.module('restapp', ['ngRoute']);

    app.config(['$routeProvider', function($routeProvider) {
        $routeProvider.
        when('/home', {
                templateUrl: 'partials/main.html',
                controller: 'MainController'
            })
            .otherwise({
                redirectTo: '/home'
            });
    }]);


    app.controller('MainController', ['$scope', function($scope) {

    }]);


    /** DEVELOPERS **/

    /** BUGS **/

    /** STORIES **/

    /** ASSIGNMENTS **/


})();

we also need to add angularjs functionality to our index.html file.

First change <html> to <html data-ng-app="restapp">

Then change <div class="wrapper">Content will be displayed here</div> to

<div class="wrapper" ng-controller="MainController">
  <ng-view></ng-view>
</div>

and add <script type="text/javascript" src="./js/restapp.js"></script> after

<script type="text/javascript" src="./js/angular.min.js"></script>
<script type="text/javascript" src="./js/angular-route.js"></script>
<!--add restapp-->
<script type="text/javascript" src="./js/restapp.js"></script>

Finally we need to add an html page to display content in main page. I will call this file main.html and I'll save it into static/partials/ folder.

<div class="row">
    <div class="text-left col-sm-6 col-md-3 col-lg-3 col-xl-2 no-space" style="padding: 0px 10px;">
        <div class="thumbnail">
            <div class="text-center" style="font-size:36pt;">
                <i class="fa fa-users"></i>
            </div>
            <div class="caption" style="padding:20px;">
                <h5 class="post-caption text-center">
                    <a class="" href="#/developer">Developers</a>
                </h5>
                <p style="font-size:14px;">
                    You can add, remove or update developers in this section.
                    <a href="#/developer">Go</a><br><br>
                </p>
            </div>
        </div>
    </div>

    <div class="text-left col-sm-6 col-md-3 col-lg-3 col-xl-2 no-space" style="padding: 0px 10px;">
        <div class="thumbnail">
            <div class="text-center" style="font-size:36pt;">
                <i class="fa fa-bug"></i>
            </div>
            <div class="caption" style="padding:20px;">
                <h5 class="post-caption text-center">
                    <a class="" href="#/bug">Bugs</a>
                </h5>
                <p style="font-size:14px;">
                    You can manage bugs in this section
                    <a href="#/bug">Go</a><br><br>
                </p>
            </div>
        </div>
    </div>

    <div class="text-left col-sm-6 col-md-3 col-lg-3 col-xl-2 no-space" style="padding: 0px 10px;">
        <div class="thumbnail">
            <div class="text-center" style="font-size:36pt;">
                <i class="fa fa-clone"></i>
            </div>
            <div class="caption" style="padding:20px;">
                <h5 class="post-caption text-center">
                    <a class="" href="#/story">Stories</a>
                </h5>
                <p style="font-size:14px;">
                    You can manage stories in this section
                    <a href="#/story">Go</a><br><br>
                </p>
            </div>
        </div>
    </div>

    <div class="text-left col-sm-6 col-md-3 col-lg-3 col-xl-2 no-space" style="padding: 0px 10px;">
        <div class="thumbnail">
            <div class="text-center" style="font-size:36pt;">
                <i class="fa fa-connectdevelop" aria-hidden="true"></i>
            </div>
            <div class="caption" style="padding:20px;">
                <h5 class="post-caption text-center">
                    <a class="" href="#/assign">Assign</a>
                </h5>
                <p style="font-size:14px;">
                    Assign bugs and stories to developers
                    <a href="#/assign">Go</a><br><br>
                </p>
            </div>
        </div>
    </div>
</div>

now we can run our application mvn spring-boot:run and you should see this screen below.

Here is third commit for adding AngularJs to the project.





Section 3 : Implementing "Developers" Functionality

In this section there will be three layers in the application:

  • Controller Layer

  • Service Layer

  • Repository/Dao Layer

The requests from clients will be handled in controllers. Than service layer will make some bussiness logic operations, finally Dao or Repository Layer will handle database operations.

First I will create a package com.ferdisonmezay.issuetracker.entity for Entity classes. Then I'm going to create two Java Entity classes.

Our Developers will only have id and name attributes. But since every entity will have ìd attribute in this project, first I will create a class called BaseEntity.java, and extend this class from other entity classes.

Here is BaseEntity.java file:

package com.ferdisonmezay.issuetracker.entity;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public class BaseEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name="ID")
  private Integer id;

  public Integer getId() { return id; }
  public void setId(Integer id) {	this.id = id; }
}

And here is DeveloperEntity.java class:

package com.ferdisonmezay.issuetracker.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name="DEVELOPERS")
public class DeveloperEntity extends BaseEntity {

  @Column(name="name")
  private String name;

  public DeveloperEntity() {}
  public DeveloperEntity(String name) { this.name = name; }
  public String getName() { return name; }
  public void setName(String name) { this.name = name; }
}

Next, for database operations, I will create an interface, called DeveloperDao.java in the package com.ferdisonmezay.issuetracker.dao, which extends JpaRepository<EntityClass, Integer>.

package com.ferdisonmezay.issuetracker.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.ferdisonmezay.issuetracker.entity.DeveloperEntity;

@Repository
public interface DeveloperDao extends JpaRepository<DeveloperEntity, Integer> {

}

This class does not contain any functions, we will use methods from JpaRepository class.

In some applications there's no need to add a service layer between the controller and Dao classes, but I will add this layer. Maybe some of you will want to add some bussiness logic in Service classes.

So here is the DeveloperService.java file, in com.ferdisonmezay.issuetracker.service package.

package com.ferdisonmezay.issuetracker.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ferdisonmezay.issuetracker.dao.DeveloperDao;
import com.ferdisonmezay.issuetracker.entity.DeveloperEntity;

@Service
public class DeveloperService{

  @Autowired
  private DeveloperDao developerDao;

  public DeveloperEntity add(String name) {
    DeveloperEntity developer = new DeveloperEntity(name);
    return developerDao.save(developer);
  }

  public List<DeveloperEntity> getDevelopers(){ return developerDao.findAll(); }
  public DeveloperEntity getById(Integer id) { return developerDao.findOne(id); }
  public DeveloperEntity update(DeveloperEntity developer) { return developerDao.save(developer); }
  public void delete(Integer developerId) { developerDao.delete(developerId); }
  public Long getDeveloperCount() { return developerDao.count(); }
}

Finally we need to create our RESTful services. To handle requests, I will create DeveloperController.java annotated with @RestController in the package com.ferdisonmezay.issuetracker.controller

package com.ferdisonmezay.issuetracker.controller;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;
import com.ferdisonmezay.issuetracker.entity.DeveloperEntity;
import com.ferdisonmezay.issuetracker.service.DeveloperService;

@RestController
@RequestMapping("/developer")
public class DeveloperController {

  @Autowired
  private DeveloperService developerService;

  @RequestMapping(value="list", method = RequestMethod.GET)
  public List<DeveloperEntity> getDeveloperList() {
    return developerService.getDevelopers();
  }

  @RequestMapping(value = "create", method = RequestMethod.POST)
  public DeveloperEntity create(@RequestBody String name) {
    return developerService.add(name);
  }

  @RequestMapping(value = "update/{id}", method = RequestMethod.PUT)
  public DeveloperEntity update(@RequestBody DeveloperEntity developer) {
    return developerService.update(developer);
  }

  @RequestMapping(value = "delete/{id}", method = RequestMethod.DELETE)
  public void delete(@PathVariable(value = "id") Integer developerId) {
    developerService.delete(developerId);
  }
}

Now we need to add CRUD operation functions for developers section to AngularJs file, which we called restapp.js

First add routing rule for developer url.

.when('/developer', {
      templateUrl: 'partials/developers.html',
      controller: 'DevelopersController'
  })

Then add DevelopersController and DevelopersService functionalities. to restapp.js file.

/** DEVELOPERS **/
app.controller('DevelopersController', ['$scope', 'DevelopersService', function($scope, DevelopersService) {

    function resetParams() {
        $scope.actionName = "New Developer";
        $scope.submitButtonLabel = "Insert";
        $scope.currentDeveloper = {
            id: null,
            "name": null
        };
        $scope.isFormClean = true;
        $scope.name = "";
    }

    function developerList() {
        DevelopersService.list()
            .then(function(response) {
                $scope.developers = response.data;
            });
    }

    function resetParamsAndLoadDeveloperList() {
        resetParams();
        developerList();
    }

    $scope.selectDeveloper = function(developer) {
        $scope.actionName = "Update Developer";
        $scope.submitButtonLabel = "Update";
        $scope.currentDeveloper = developer;
        $scope.name = $scope.currentDeveloper.name;
        $scope.isFormClean = false;
    };

    $scope.submit = function() {
        if ($scope.isFormClean) {
            $scope.insert($scope.name);
        } else  {
            $scope.currentDeveloper.name = $scope.name;
            $scope.update($scope.currentDeveloper);
        }
        resetParamsAndLoadDeveloperList();
    };

    $scope.insert = function(name) {
        DevelopersService.insert(name)
            .then(function(response) {
                resetParamsAndLoadDeveloperList();
            });
    };

    $scope.update = function(developer) {
        DevelopersService.update(developer)
            .then(function(response) {
                resetParamsAndLoadDeveloperList();
            });
    };

    $scope.delete = function(developerId) {
        DevelopersService.delete(developerId)
            .then(function(response) {
                resetParamsAndLoadDeveloperList();
            });
    };

    $scope.cancel = function() {
        resetParams();
    };

    resetParamsAndLoadDeveloperList();

}]);

app.service('DevelopersService', function($http) {
    var service = this;
    var path = '/developer/';

    function getPath() {
        return path;
    }

    function getUrl(action) {
        return getPath() + action;
    }

    service.list = function() {
        return $http.get(getUrl('list'));
    };

    service.insert = function(name) {
        return $http.post(getUrl('create'), name);
    };

    service.update = function(developer) {
        return $http.put(getUrl('update/' + developer.id), developer);
    };

    service.delete = function(id) {
        return $http.delete(getUrl('delete/' + id), {
            'id': id
        });
    };
});

/** BUGS **/

Finally add an html file in partials folder called developers.html to display developer list and make CRUD operations on developers.

<ol class="breadcrumb">
  <li><a href="#/home"><i class="fa fa-home" aria-hidden="true"></i></a></li>
  <li class="active">Developers</li>
</ol>

<div class="row" data-ng-controller="DevelopersController">
  <div class="col-md-6">
    <h4 class="text-center">Developers</h4>
    <div class="white-background" style="padding:15px;">
	    <table class="table table-hover table-condensed">
	      <thead>
	        <tr>
	          <th>#</th>
	          <th>Name</th>
	          <th>Actions</th>
	         </tr>
	      </thead>
	      <tbody>
	        <tr ng-repeat="developer in developers" ng-class="(developer.id == currentDeveloper.id) ? 'warning' : ''">
	          <td>{{developer.id}}</td>
	          <td width="80%">{{developer.name}}</td>
		        <td  width="5%" style="text-align:center;">
	            <a class="btn btn-xs btn-success" ng-click="selectDeveloper(developer)"><i class="fa fa-pencil-square-o "></i></a>
		        </td>
	         </tr>        
	      </tbody>
	    </table>
    </div>
  </div>
  <div class="col-md-1"></div>
  <div class="col-md-5">
    <h4 style="color:#B80D57;">{{actionName}}</h4>
    <div class="white-background" style="padding:20px;">
    <form name="developerForm" ng-submit="submit()" method="POST">
      <div class="row">
        <div class="col-md-12">
          <div class="form-group">
            <label class="control-label" for="name">Name</label>
            <input value="" name="name" id="name" type="text" ng-model="name" placeholder="" class="form-control">
          </div>
        </div>
      </div>
      <div class="row">
        <div class="col-md-12 text-right">
          <input class="btn btn-success btn-sm" type="submit" id="submit" value="{{submitButtonLabel}}" />
          <a ng-if="currentDeveloper.id" ng-click="delete(currentDeveloper.id)" class="btn btn-danger btn-sm">Delete</a>
          <a ng-if="currentDeveloper.id" ng-click="cancel()" class="btn btn-default btn-sm">Cancel</a>
        </div>
      </div>
    </form>
    </div>
  </div>
</div>

After the latest changes, when you click on "DEVELOPERS" link, you should be seeing the screen below.

Here is next, commit for adding developer crud operations.

Swagger Rest API Documentation

You can view API documentation and test the API by swagger-ui, which you can access from : http://localhost:8080/swagger-ui.html after you run the application.

Data Initialization and Storage

Initial data are loaded into application from import.sql file. This application uses HSQLDB as database, when you start the application, all scripts from the specified file are executed, and data is imported to the application.

Note: When you stop the application, data that you inserted using the UI will be lost.

Summary

In this tutorial I've created a spring boot application, and explained how to create a java web application with Spring framework and AngularJs.

Tutorial contains only "Developers" section, which you can add, update, delete and view developers. I've also added functionality for creating, updating and deleting bugs and stories. There's also a page to auto-assign stories to developers.

You can get the latest source from Github.