It's good to be awesome
Nikola Todorovic
  • Home
  • Hire Me
  • Nikola's Blog
  • Professional Career
  • Biography
  • Photo Gallery
  • Contact

RAILS 4 + ANGULARJS + JSON WEB TOKEN AUTHENTICATION

5/8/2015

2 Comments

 
Recently I was contacted by Toptal to join their network as a developer. I heard some good stuff about them before so I decided to give it a go. Toptal is "a network comprised of the most thoroughly screened, talented freelance engineers in the world". To enter the network you have to pass 4 tests. The first one is an interview - a simple English test, the second one is Codility test. The third one is a one-to-one test with a senior developer from the network when you need to solve two problems at limited time. And the last one is to create a full application.
Anyway, I am not going to talk about Toptal, rather than the project they gave me to do as the last part of the screening process. The full text of the project as well as the solution you can find on my GitHub. In this post I will only go through the main things and make some general remarks how development should be done and what you should pay attention, I won't cover every detail because there is too much code. For all the details you have code on GitHub.

Authentication

Since I needed to create API with every operation done using JavaScript I decided to use Angular to create a SPA. And since there is an API and SPA I decided to authenticate users using tokens. I didn't want to use Devise since the basic gem doesn't offer token authentication and then you need to add some other gem to be able to do that so I decided to use so called JWT (JSON web token). More about that technology you can find here.
require 'jwt'

module AuthToken
  def AuthToken.issue_token(payload)
    payload[:exp] = Time.now.to_i + 4 * 3600
    JWT.encode payload, Rails.application.secrets.secret_key_base
  end

  def AuthToken.valid?(token)
    begin
      JWT.decode token, Rails.application.secrets.secret_key_base
    rescue
      false
    end
  end
end
Token generation, encoding and decoding is very simple using existing ruby gem. Only thing you need to do is gather everything in a whole. The general idea when user needs to be authenticated is to generate an API call with the parameters of email and password. If it turns out that this is the valid user, the token is being generated and returned to the user as a JSON response. That response also contains a data what user it is (user_id). The whole token is saved in the browser's local storage. Instead, a token can be stored in a cookie (therefore cookie is used as a storage), more about that you can find on this link.
class AuthController < ApplicationController
  require 'auth_token'

  layout false

  def register
    @user = User.new(user_params)
    if @user.save
      @token = AuthToken.issue_token({ user_id: @user.id })
    else
      render json: { errors: @user.errors }, status: :unauthorized
    end
  end

  def authenticate
    @user = User.find_by(email: params[:email].downcase)
    if @user && @user.authenticate(params[:password])
      @token = AuthToken.issue_token({ user_id: @user.id })
    else
      render json: { error: "Invalid email/password combination" }, status: :unauthorized
    end
  end

  def token_status
    token = params[:token]
    if AuthToken.valid? token
      head 200
    else
      head 401
    end
  end

  private

    def user_params
      params.permit(:first_name, :last_name, :email, :password)
    end
    
end
Then, each time the client sends a request to the API, the token is being added to the header of that request. To do this, an interceptor is created on Angular side which intercepts each request and does this part of the job.
@topTalApp.factory('AuthInterceptor', ($location, $rootScope, $q, $injector) ->
  
  authInterceptor = {

    request: (config) ->
      token = undefined
      if localStorage.getItem('auth_token')
        token = angular.fromJson(localStorage.getItem('auth_token')).token
      if token
        config.headers.Authorization = 'Bearer ' + token
      config

    responseError: (response) ->
      if response.status == 401
        localStorage.removeItem 'auth_token'
        $rootScope.errorMsg = response.data.error
        $location.path '/login'
      if response.status == 403
        $rootScope.errorMsg = response.data.error
        $location.path '/'
      $q.reject response

  }
  
  authInterceptor

).config ($httpProvider) ->
  $httpProvider.interceptors.push 'AuthInterceptor'
  return
On the API side, the token is being extracted from the header of that request and checked whether it is valid (the expiration time is being set on the token). If it is valid than user_id is being used to get data for current_user. If token is invalid or there isn't any token, API returns http code 401 - which prevents unauthorized access to the API if someone is not authenticated...
module Api
  class BaseController < ApplicationController
    require 'auth_token'

    before_action :authenticate

    # jbuilder needs this
    layout false

    private

      def authenticate
        begin
          token = request.headers['Authorization'].split(' ').last
          payload, header = AuthToken.valid?(token)
          @current_user = User.find_by(id: payload['user_id'])
        rescue
          render json: { error: 'You need to login or signup first' }, status: :unauthorized
        end
      end

  end
end
And let's not forget about routes. This is how I defined routes. This is pretty straightforward but if you need some extra explanation about it you can find an excellent RailsCast episode about APIs.
Rails.application.routes.draw do
  root 'home#index'

  namespace :api, defaults: {format: :json} do
    resources :users, except: [:new, :edit]
    resources :expenses, except: [:new, :edit] do
      collection do 
        get :weekly
      end
    end
  end

  post '/auth/register', to: 'auth#register', defaults: {format: :json}
  post '/auth/authenticate', to: 'auth#authenticate', defaults: {format: :json}
  get '/auth/token_status', to: 'auth#token_status', defaults: {format: :json}

end

Authorization

Authorization is certainly another important thing in every application but unfortunately it is something that is not paying too much attention. Of course, everyone has their own idea of how authorization should be implemented and I don't think there is any general best practice but I think that this approach, which I applied here, is more than good. It is necessary to do authorization on the API side and also in the web application. On API side because calls can be directed only to an API without any application involved and within application users without enough permissions should be forbidden access to the certain parts of the application as well as the forms for inserting new data.
I defined three types of user in the application - regular user, admin and user manager. A regular user has permission to only list her expenses and to create them, user manager has the right to list all the users in the application, edit them and create a new one, admin has the right to create expenses for herself and to list and edit expenses of all users in the system as well as to create new and edit existing users.
class User < ActiveRecord::Base

  has_secure_password

  validates :email, :password_digest, presence: true
  validates :email, uniqueness: { case_sensitive: false }
  
  validates :password, length: { minimum: 5 }

  has_many :expenses
  
  belongs_to :role

  ROLES = {
    REGULAR: 1,
    ADMIN: 2,
    MANAGER: 3
  }

  def is_regular
    self.id_role == ROLES[:REGULAR]
  end

  def is_admin
    self.id_role == ROLES[:ADMIN]
  end
  
  def is_manager
    self.id_role == ROLES[:MANAGER]
  end

end

- Api

I didn't want to use Cancancan or Pundit or similar gems because I wanted to save myself some time and the authorization on the API is more than simple. All you have to do is to create a before_action filter and each controller then has to define who has the right to access the actions within it. If someone doesn't have permissions then you need to return http code :forbidden. This of course can't be tested in the application but there are tests you can write to check if everything is fine.
module Api
  class UsersController < Api::BaseController
    before_action :is_authorized?

    private

      def is_authorized?
        if @current_user.is_regular
          render json: { error: "Doesn't have permissions" }, status: :forbidden
          return
        end
      end

  end
end

- Angular

Angular part is a little more complex... First you should remove links from menu if certain user doesn't have permissions and then you should also disable the possibility for the same users to manually enter routes in the browser, which would allow them to visit those pages. It is pretty much easy to disable links in the menu but to do the other part you need to put some more effort in it. The most important part can be found in the file angular/services/auth.coffee:
@topTalApp.factory 'Auth', ['$http', 'CurrentUser', 'ROLES', ($http, CurrentUser, ROLES) ->

  currentUser = CurrentUser

  token = localStorage.getItem('auth_token')

  {
    isRegularUser: (user) ->
      user.getRole() == ROLES.REGULAR

    isAdminUser: (user) ->
      user.getRole() == ROLES.ADMIN

    isManagerUser: (user) ->
      user.getRole() == ROLES.MANAGER

    isAuthorized: (permissions) ->
      i = 0
      while i < permissions.length
        switch permissions[i]
          when 'REGULAR'
            return true if currentUser.getRole() == ROLES.REGULAR
          when 'ADMIN'
            return true if currentUser.getRole() == ROLES.ADMIN
          when 'MANAGER'
            return true if currentUser.getRole() == ROLES.MANAGER
        i += 1
      return false
  }

]
Then, you should listen to a $routeChangeStart event and disable access to a page if user doesn't have needed permissions. The code is in the file angular/app.coffee:
@topTalApp = angular.module('topTalApp', ['ngRoute', 'rails', 'templates', 'ui.bootstrap', 'sy.bootstrap.timepicker', 'angularUtils.directives.dirPagination'])

@topTalApp

  .run [
    '$rootScope'
    '$location'
    'Auth'
    ($rootScope, $location, Auth) ->
      $rootScope.$on '$routeChangeStart', (event, next, current) ->
        if next.access != undefined and !Auth.isAuthorized(next.access.requiredPermissionsAnyOf)
          if next.templateUrl == 'expenses/expenses.html' and Auth.isAuthenticated() != null
            $location.path '/users'
          else if next.templateUrl == 'expenses/expenses.html' and Auth.isAuthenticated() == null
            $location.path '/login'
          else
            $location.path '/'
        return
      return
  ]
  
  .config(
    ($routeProvider) ->
    
      $routeProvider
        .when '/signup', {templateUrl: 'sessions/signup.html', controller: 'SignupCtrl'}
        .when '/login', {templateUrl: 'sessions/login.html', controller: 'LoginCtrl'}

        .when '/', {
          templateUrl: 'expenses/expenses.html',
          controller: 'ExpenseCtrl',
          access: requiredPermissionsAnyOf: [ 'REGULAR', 'ADMIN' ]
        }

        .when '/expenses', {
          templateUrl: 'expenses/expenses.html',
          controller: 'ExpenseCtrl',
          access: requiredPermissionsAnyOf: [ 'REGULAR', 'ADMIN' ]
        }

        .when '/new_expense', {
          templateUrl: 'expenses/new_edit_expense.html',
          controller: 'ExpenseCtrl',
          access: requiredPermissionsAnyOf: [ 'REGULAR', 'ADMIN' ]
        }

        .when '/expense/:id', {
          templateUrl: 'expenses/new_edit_expense.html',
          controller: 'ExpenseCtrl',
          access: requiredPermissionsAnyOf: [ 'REGULAR', 'ADMIN' ]
        }

        .when '/expenses/weekly', {
          templateUrl: 'expenses/weekly.html',
          controller: 'ExpenseWeeklyCtrl',
          access: requiredPermissionsAnyOf: [ 'REGULAR', 'ADMIN' ]
        }

        .when '/users', {
          templateUrl: 'users/users.html',
          controller: 'UserCtrl',
          access: requiredPermissionsAnyOf: [ 'MANAGER', 'ADMIN' ]
        }

        .when '/new_user', {
          templateUrl: 'users/new_edit_user.html',
          controller: 'UserCtrl',
          access: requiredPermissionsAnyOf: [ 'MANAGER', 'ADMIN' ]
        }

        .when '/user/:id', {
          templateUrl: 'users/new_edit_user.html',
          controller: 'UserCtrl',
          access: requiredPermissionsAnyOf: [ 'MANAGER', 'ADMIN' ]
        }

        .otherwise({redirectTo: '/'})

  )
I think this is more than elegant solution for authorization. The whole code you can find on GitHub as I mentioned before.

Rails API

When you set up the basics of the application with authentication and authorization then you must set up the API part of the Rails. There is an excellent RailsCast about it and many tutorials online so I won't go into details. Of course, you can use Grape instead of Rails but I decided to stick with Rails because I've never before worked with Grape and I wanted to save time on this part too. The thing you have to keep in mind are the routes and the fact that each API call must return JSON response. You can use active model serializer or Jbulder or you can even go without that of course. But I used Jbulder because you definitely need an easy way to properly create JSON you want to return to the user that made the request. Again I have to mention that you have a great RailsCast episode how to use Jbuilder so I won't write on this topic further.

Angular app

This was a new ground for me, I've never built a SPA before but for two weeks how long did this project last I am very satisfied with what I've achieved. There are several ways to integrate your Rails API with Angular application. Again I chose the simplest option, although probably not the best. I used Angular gem. I think that in a bigger project the best way is to completely separate API part of its SPA part and not use this gem but in this situation it served the purpose really well.
I won't post pieces of the code here because you have a whole project on GitHub, but I would like to note that I used Slim for making templates, CoffeeScript instead of a plane JavaScript because I like cleaner code and I used Bootstrap too. I made a couple of filters in order to extract information about date and time from the column in the database that stores datetime together for nicer display. Everything else is pretty straightforward.

Timezone

The part of the project that I had the most headache is the part with printing data for expenses arranged by weeks. Why was this such a big problem? As I wrote in a previous blog post, it's very important how you work with time zones in your application. The best practice is to store everything in UTC time in the database. When a user enters a date and time in the form (her local time) you have to store that time in the UTC format in the database and then when you need to display that time to the user you have to convert it back into her local time. Pretty standard stuff... Except for one little thing - the requirement is to display expenses arranged by weeks and by weeks it's meant from the user's perspective. From user's perspective a week isn't the same as a week on the server which is in UTC time. So, you have to deal with UTC time because you have to prepare JSON response in your API but in the same time you have to deal with user's local time (starting and ending of the week) and arrange JSON response according to that. If a user is, for example, in Belgrade (CET) and if she inserts an expense for Monday, July 27th at 1 AM it would be 31st week of the year according to her. But by the server's time, it is Sunday July 26th at 11 PM, which means it is 30th week for the UTC time. I solved this problem by sending time zone offset as a parameter to the API and by adding it to a time I stored in the database. You must also pay attention to the edge cases when you calculate weeks when start of a week is in one year and its ending is in the next year.
def weekly
  expenses = findByDateFromAndTo params[:datefrom], params[:dateto], :asc
  my_json = {}

  add_minutes =  - params[:timezone].to_i.minutes

  expenses.each do |expense|
    year, month, week = (expense.for_timeday + add_minutes).strftime('%Y'), (expense.for_timeday + add_minutes).strftime('%m'), (expense.for_timeday + add_minutes).strftime('%V')
    year = (year.to_i + 1).to_s if month.to_i == 12 && week.to_i == 1
    key = year + '.' + week

    analytics = my_json[key] || {}
    analytics[:expense] = analytics[:expense] || []
    analytics[:total] = expense.amount + (analytics[:total] || 0)
    analytics[:items] = 1 + (analytics[:items] || 0)
    analytics[:start] = Date.commercial(year.to_i, week.to_i, 1).to_s
    analytics[:end] = Date.commercial(year.to_i, week.to_i, 7).to_s

    my_expense = {for_timeday: expense.for_timeday, amount: expense.amount, description: expense.description, comment: expense.comment}
    analytics[:expense].push my_expense

    my_json[key] = analytics
  end

  render json: my_json.values
end

TestS

Last but not least - tests! Writing tests is very important and you shouldn't neglect it even if you have tight deadline. I did lot more here than it was expected from me but unfortunately I didn't have time to write tests for JavaScript, but the procedure is pretty standard... I used Factory Girl and Faker to create data and also Database Cleaner to clean data after running tests.
FactoryGirl.define do

  factory :expense do
    
    association :user, factory: :user_regular

    amount { Faker::Commerce.price }
    for_timeday { Faker::Time.between(100.days.ago, 1.day.ago) }
    description { Faker::Lorem.sentence }
    comment Faker::Lorem.sentence

    trait :future_time do
      for_timeday { Faker::Time.forward(5, :all) }
    end

    factory :expense_future, traits: [:future_time]

  end

end
Next, you should write unit tests - tests for models, since this is not a big database there is only two models to test, user and expense:
require 'rails_helper'

describe Expense do

  it 'has a valid factory' do
    expect(build(:expense)).to be_valid
  end

  it 'is invalid without an amount' do
    expect(build(:expense, amount: nil)).to_not be_valid
  end

  it 'is invalid without a description' do
    expect(build(:expense, description: nil)).to_not be_valid
  end

  it 'is invalid without a time' do
    expect(build(:expense, for_timeday: nil)).to_not be_valid
  end

  it 'is invalid with a time set in the future' do
    expect(build(:expense_future)).to_not be_valid
  end

  it 'should belong to a user' do
    expense = build(:expense)
    user = build(:user)
    expense.user = user
    expect(expense.user).to be user
  end

end
And finally functional tests - for testing your controllers. I am especially proud of this part of the application since I covered entire API - authentication, authorization as well as creating expenses, users and so on...
require 'rails_helper'

describe AuthController do

  # because of the jBuilder I need to render views
  render_views

  describe 'POST #register' do

    context 'with valid credentials' do

      it 'returns user id' do
        #build a user but does not save it into the database
        user = build(:user_regular)
        post :register, { email: user.email, password: user.password, format: :json }
        expect(response.status).to eq 200
        parsed_response = JSON.parse response.body
        expect(parsed_response['user']['id']).to_not be_nil
      end

    end

    context 'with invalid credentials' do

      it 'does not have email' do
        post :register, { password: "pass", format: :json }
        expect(response.status).to eq 401
        parsed_response = JSON.parse response.body
        expect(parsed_response['errors']).to_not be_nil
        expect(parsed_response['errors']['email'][0]).to eq "can't be blank"
      end

      it 'does not have password' do
        post :register, { email: "email@email.com", format: :json }
        expect(response.status).to eq 401
        parsed_response = JSON.parse response.body
        expect(parsed_response['errors']).to_not be_nil
        expect(parsed_response['errors']['password'][0]).to eq "can't be blank"
      end

    end

  end


  describe 'POST #authenticate' do

    context 'with valid credentials' do

      it 'returns token' do
        user = create(:user_regular)
        post :authenticate, { email: user.email, password: user.password, format: :json }
        expect(response.status).to eq 200
        parsed_response = JSON.parse response.body
        expect(parsed_response['token']).to_not be_nil
      end

      it 'returns token with 3 parts separated by comas' do
        user = create(:user_regular)
        post :authenticate, { email: user.email, password: user.password, format: :json }
        expect(response.status).to eq 200
        parsed_response = JSON.parse response.body
        expect(parsed_response['token'].split('.').count).to eq 3
      end

      it 'returns first name and last name of the user' do
        user = create(:user_regular)
        post :authenticate, { email: user.email, password: user.password, format: :json }
        expect(response.status).to eq 200
        parsed_response = JSON.parse response.body
        expect(parsed_response['user']['first_name']).to eq user.first_name
        expect(parsed_response['user']['last_name']).to eq user.last_name
      end

    end

    context 'with invalid credentials' do

      it 'does not return token' do
        user = create(:user_regular)
        post :authenticate, { email: "no_" + user.email, password: user.password, format: :json }
        expect(response.status).to eq 401
      end

    end

  end

  describe 'POST #token_status' do

    context 'with valid token' do

      it 'returns OK code' do
        user = create(:user_regular)
        token = AuthToken.issue_token({ user_id: user.id })
        post :token_status, { token: token, format: :json }
        expect(response.status).to eq 200
      end

    end

  end

end
Again, the rest of the code you can find on GitHub :)

The end

If you are a great developer and you want to try yourself to do some similar project, and if you want to work for the Toptal as a freelance developer, to fulfill your dreams to work from home or some exotic island you can sign up here, I enjoyed participating in such a process of selection of candidates.
2 Comments

RAILS 4 + HAML + ZURB FOUNDATION 5 + SASS + POSTGRES

21/8/2014

1 Comment

 
Hello again. It's been awhile since I wrote something but I am too busy building my startup Warrantly so I really don't have time to write often. Nevertheless, now it's time to share something new. Being a Java developer for years I switched to Ruby on Rails a year ago because we decided it could be the best tool to build a startup. Boy it was a great decision! Since then I am really enjoying exploring Ruby and Rails and I don't have any desire going back to Java again.
Picture
Don't get me wrong, Java is a great programming language but with Rails you can create a new functional website in literally minutes! And with Java you have to decide which framework to use, how to connect everything to work properly and you will spend hours figuring out that. A year and a half ago I wrote how to start a new project with Spring because it is too complicated and I wanted to write down few steps you need to do to connect everything to work properly. Now I will show you how to do the same thing with Rails and it is so damn simple you will need only 15 minutes.

PREREQUISITE

First you need to install Ruby and Rails 4 and you can find tutorials everywhere how to do that so I won't explain that part. You don't need any IDE to code your program, you can use any text editor (I prefer Sublime) and you'll need a console. Now it could be a little more complicated if you are using Windows but it won't make you some serious trouble.

CREATE NEW APP AND ADD GEMS

In your console, go to the directory you want to use to store your project and type this:
Picture
And you created your app! Next step is to add gems: foundation-rails, sass-rails, haml-rails, pg (for Postgres database) and add simple_form because I highly recommend using it in your app.
Picture

CONFIGURE DATABASE

If you want to connect your project with a database then you need to configure your database.yml file located under config directory:
Picture

ZURB FOUNDATION + HAML

Since we are using ZURB and HAML now you need to go to your console again and run this line:
Picture
We are installing Foundation and overriding application layout file under view directory because we want to use HAML. Now under assets/stylesheets directory find the file application.css and rename it to application.scss. You don't need to do that but if you want to use sassy css in your whole app it is good practice to do this in this step. By the way, you are probably asking what the hell are sass and scss, are they the same thing or not? I won't explain here more on this subject but you can find great explanation about them in one railscast episode.

SIMPLE FORM

Installing simple form gem is also one liner:
Picture

ScafFold + DATABASE MIGRATION

This was really skinny tutorial but I don't want to write 5 pages long post about simple stuff. Basically the application is ready to use. You can now create model, views, migration files just using one rails command, the powerful scaffold:
Picture
In your migration file which is created with this command (under db/migrate) you can add some code, this for example:
Picture
To reflect these changes to your database you need to run three commands:
Picture
Picture
Picture
With these commands you created actual database, load you schema, populated it with some seed data (if you have any under seeds.rb file) and ran your migration file to create a database table. Find more about these commands, you will need them during your development.

THE END

I hope this can help you like my previous post about SPRING did it. If you plan to launch some startup, maybe Rails is the right tool for you.
1 Comment

WARRANTLY

5/6/2013

0 Comments

 
Picture
Yes it's true - I founded a startup! This is the reason why I neglected writing a blog for last couple of months. So, in the future, I'll write mainly on startup topics. For months now I tried to think of a good idea for sturtup. After couple of very bad ideas and couple of less bad ideas but not good enough, I finally got the right one. During the last couple of months this idea evolved into well structured product. There are 5 guys in the team, we are almost at the end of building our MVP and we are already in the talk with few companies that are interested in using this software. In the next couple of lines I'll describe what we are building.

Warrant.ly is a system that allows retailers to issue online warranties to customers instead of paper ones. In this way, all problems that may occur with the purchased device within the warranty period can be easily solved through the website. The customer will report the problem to the retailer and they can arrange the repair easily through the website as well. The customer will also be able to check its status. What is more important to the customers, they will be able to keep their warranties in this system even though they were issued the old-fashioned way – in a paper form. The customer just needs to enter basic information about the device and to take a picture of the warranty.
Picture
This is just a basic description, retailers will get much more possibilities in this software so they can really improve their business. They can create and offer extended warranty plans to their customers and also send some offers about specific devices to targeted users.

We already presented our startup to some investors. We attended mini Seedcamp event in Belgrade in April and we got very good feedback although we didn't manage to get into Seedcamp program. Also, some other startup incubators in the region contacted us about possibilities of joining them. We'll see what's the best option for us at the moment.

That's it for now, come back soon on this site to find more news about this startup and startups in general. And of course, if you are a retailer and you are interested about possibilities our software offers, please contact me. I guarantee that your business can only improve and progress by using Warrantly.
0 Comments

SPRING 3 + HIBERNATE 4 + MYSQL + MAVEN + TOMCAT

27/12/2012

9 Comments

 
I've started several times by now to work on the Spring project from scratch, where I had to combine several different tools so that I can begin to program something concrete. That procedure eats time, and every time I forget something how I done it previously. Since I started writing this blog, it would be probably smart to write down the whole procedure if I forget anything next time, and perhaps also help someone to start a project and not spend hours banging their heads on how to combine a couple of tools in one whole project.

Prerequisite

This won't be literally step-by-step tutorial, I expect that everybody can download the latest versions of these tools that I will use. You will need Eclipse, Maven, Tomcat, MySql and you should download and install the latest versions. You don't need to download Spring and Hibernate since we will tell Maven to download it for us (we will configure XML file). And finally you should download Twitter Bootstrap because I will use it for front-end part in our JSP. If you haven't heard for this tool, it is basically a set of CSS and JavaScript files developed by Twitter so programmers can easily and fast create some fancy pages and features on these pages. You will also need jQuery because Twitter Bootstrap uses it.

Eclipse + Maven + project structure

Let's roll! When you open Eclipse you should create Maven project, BUT I suggest that you create simple Java project. After that you should convert it into Maven project. I like to do this way because when you create maven project the wizard will ask you to enter many confusing things about the project, and you could be puzzled with it if you didn't work with Maven before. When you create Java project, you only need to enter project name, the wizard for converting this project to Maven will do the rest. For this converting you should select your project and under configure submenu you should find 'Convert to Maven project'. Now you have your Maven project. Next thing you should do is to set your project structure, that is to create all the folders in the proper places of your project that you will be needed to launch the site in the browser. I suggest that you first delete the source folder 'src' and create your own source folders: 'src/main/java', 'src/main/webapp' and 'src/main/resources'. After that you should create the other folders (css, img, js) under 'src/main/webapp', you can see the structure on next picture:
Picture
In this step you should also create landing-page.jsp and that will be the first page that is displayed in the browser when you start your project. For now, you can insert in this file any text you want, you just want to test if that text is displayed in the browser when you start your project. Later we will modify this file.

The next thing you need to add here is three files: applicationContext.xml, spring-servlet.xml and web.xml. Now, you should edit these files if you want to run the project. I am not going to post the code that you need to add in these files, but you can download my project from: https://github.com/nikolatodorovic/spring_hibernate_mysql_maven_tomcat and you can see the content of these files. There are plenty of explanations about these files on the Internet and it would be probably good to read them.

The last thing, but maybe the most important is to configure pom.xml file. It is the main file for Maven and it contains information about the project and configuration details that Maven uses to build the project. In this file, you are telling Maven to use Spring and other tools. Therefore you don't need to download anything about Spring or Hibernate, only you need to do is to configure this file and Maven will do it for you. Again, I won't post code here, you should download it and copy/past from my project. Every other explanation about this you can find across the Internet.

Maven build + Tomcat

So, we now want to start this project and we need Tomcat for that. But first we need to build it. You can easily do this by using Maven commands. I recommend that you create run configuration with defined goals and after that you can build your project within Eclipse, you don't need to use command line. When you create new run configuration you should select your project (base directory) and enter 'clean install war:inplace' in goals field. That command will create WAR file from your files.

Now we have WAR file and we need to start Tomcat instance so that we can deploy this WAR file to that instance. You can copy and past this WAR file in the folder: 'tomcat_home\webapps' but don't do that. Every time you make some change on any file inside your project, you need to stop Tomcat instance, do another Maven build, copy/past new WAR file in this location and start Tomcat instance again. This is really pain in the ass... Instead of that, you should configure your Tomcat so it can read your files from your workspace. And if you do that, when you modify something in your JSP while your Tomcat instance is running you even don't need to stop that instance, do another Maven build and start Tomcat again. You just need to refresh your browser to see those changes. This procedure saves time incredibly! And it is very easy to accomplish this, you need to create one XML file with the name of your project, put it in: 'tomcat_home\conf\Catalina\localhost' and insert this code in the file:

<Context docBase="D:\Workspace\Eclipse\MyFramework\src\main\webapp"></Context>

Of course, you need to set your own path. By doing this, you are telling Tomcat where is located your source folder with JSP files, CSS files, JS files... When you do that you only need to restart Tomcat if you make some changes on the back-end, not on the front-end.

Now this is the time to test if this is working. If you did everything correct, when you enter your project name in the browser, your login page should be displayed. You can change some text in that file so you can test if refreshing of the browser displays changes on that page.

Spring

Next thing we'll do is to create a few basic pages, create layout for our project and use a little of Twitter Bootstrap for front-end. You should have a project structure as it is shown on the next picture:
Picture
From this picture and downloaded files, you can see what you should do with Twitter Bootstrap's files. You should also create controller files where is all the logic located. These are very simple files so I don't need to explain how it works, I hope you can manage it yourself. Be sure to set up your pom.xml file correctly to use Spring.

Hibernate + mySql

Now let's go to another step - adding some database in the project. If you downloaded this project from gitHub you can find a file in it called database.sql. You should run that file on your own database so you can configure this project correctly. When you install mySql, you should choose what client you will use for managing you databases. I am using Heidi (www.heidisql.com), but you can use whatever you want... After creating this database, you should open file jdbc.properties located in WEB-INF folder and edit it according to your database connection parameters.

After you created database, you need to configure pom.xml file so you can use Hibernate in your project. If you copied my pom file, you should be fine. At the end you have to create hibernate.cfg.xml file under 'src/main/resources' and copy/past content from my project. Now it is all set up for using Hibernate and you can create model classes, DAO classes and service classes. This is a very good procedure how you can organize your code, but you can organize it anyway you like.

The End

And that's basically it. I won't go into details on how to use Spring, Hibernate, you can find all the necessary infos on the Web. Also, study my project and you can learn how to set up your configuration files, how to add pages, entity classes and so on... Since you read this this far, I hope it was useful :) Please leave a comment if you have any suggestion.
9 Comments

My First Blog Post

2/11/2012

4 Comments

 
Picture
Hi everyone! This is my first blog post ever. After several years having very ugly looking personal website, I decided it's time to make some radical changes and to launch a completely new presentation. So, few days ago I started looking for a easy-to-use tool that I can use to build some nice looking but simple website. And I came across Weebly. It turned out that this is really cool online tool with which you can create website within a few hours! Another great thing about Weebly is that you get free web hosting on their server when you create a site. Only you need to do is to create some good content, choose a theme and it is ready to go.

The next thing that you should consider is to choose your web address. Weebly lets you use address in a form "yourAddress.weebly.com". If you don't like it and you want to have some cool address, you can always register some domain. I finally decided to register www.nikolatodorovic.com because I don't want some wacko use that cool name for website instead of me... You have plenty of sites where you can register your domain, maybe the best known is goDaddy, but I decided to register it on nameCheap.com. It cost around $10/year and domain name was available to use after only few minutes. Some domestic sites for registration say that you need to wait for 24/48 h which is really stupid... Another cool thing about this nameCheap site is that when you spend some amount of money on their site (maybe about 50$) you can use 'coupon code' which allows you to have a discount when you purchase some service. So, with that coupon you can buy a domain name for about $4/year.

The last thing before publishing a site is to connect your domain to website created with Weebly. That procedure requires only few minutes since you have to enter IP addresses which Weebly provides to you to specific fields on your domain registrar's control panel. All the instructions you need is on Weebly's website so if you ever wanted to create your own personal website, this is your chance. 


4 Comments

    Nikola Todorovic

    Software developer from Belgrade, founder of Warrantly.

    Archives

    December 2015
    August 2015
    March 2015
    January 2015
    December 2014
    October 2014
    September 2014
    August 2014
    June 2013
    December 2012
    November 2012

    Categories

    All
    Ajax
    Angular
    API
    DataTables
    Hibernate
    Holiday
    Maven
    Metaprogramming
    Mysql
    Oracle
    Postgres
    Rails
    Ruby
    Sea
    Software
    Spring
    Startup
    Tomcat
    Turkey
    Website

    RSS Feed

Powered by Create your own unique website with customizable templates.