Thursday, August 15, 2013

Using Drupal's hook_theme() for custom theming

This is a short post on how to use Drupal's hook_theme() to customize a layout within your own custom module.

Start off with implementing the hook_theme() function in your module:

/**
 * Implements hook_theme()
 * @param type $existing
 * @param type $type
 * @param type $theme
 * @param type $path
 * @return array
 */
function mypm_theme($existing, $type, $theme, $path) {
  $themes = array(
    'chat' => array(
      'variables' => array(
        'message' => NULL,
        'author' => NULL,
        'date_posted' => NULL,
        'is_sender' => NULL,
        'product_url' => NULL,
      )
    ),
  );

  return $themes;
}

What I've declared in the function is that I have a custom theme function called "chat". It takes in 5 variables defined in the variables array. As always, remember to clear Drupal's cache once you've defined a new theme function. Now that we have declared our theme function, we'll have to implement it:

function theme_chat($variables) {
  $main_style = 'chat-holder';

  if (isset($variables['is_sender'])) {
    if ($variables['is_sender'] == TRUE) {
      $main_style .= ' chat-holder-sent';
    }
    else {
      $main_style .= ' chat-holder-received';
    }
  }
  else {
    $main_style .= ' chat-holder-received';
  }

  $output = '
'; $output .= '
'; $output .= $variables['author']; $output .= '
'; $output .= '
'; $output .= format_date($variables['date_posted']); $output .= '
'; $output .= '
'; $output .= nl2br(check_plain($variables['message'])); $output .= '
'; $output .= $variables['product_url']; $output .= '
'; return $output; }

The theme_chat function builds the HTML output based on the variables passed in. You can now use this theme function in your form or normal page callback like below:

    $chat_output .= theme('chat',
            array(
                'message' => $chat->message,
                'author' => $chat->author_name,
                'date_posted' => $chat->created,
                'is_sender' => $chat->sender,
                'product_url' => $chat->product_url,
            ));

Wednesday, August 14, 2013

Spring 3 MVC + Hibernate 4 + JdbcTemplate + Partial REST controller (Part 3)

Last part of the article is to use either Hibernate or Spring's JdbcTemplate to retrieve data. The following class is the BaseDao where DataSource and Hibernate SessionFactory are injected and used by concrete DAO classes:

package com.techtots.services.api.dao;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

public abstract class BaseDao {

	@Autowired
	protected SessionFactory sessionFactory;
	
	protected DataSource dataSource;

	protected JdbcTemplate jdbcTemplate;
	protected NamedParameterJdbcTemplate namedParameterJdbcTemplate;

	public DataSource getDataSource() {
		return dataSource;
	}

	@Autowired
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
		this.jdbcTemplate = new JdbcTemplate(this.dataSource);
		this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(this.dataSource);
		
	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
		return namedParameterJdbcTemplate;
	}

}

Once we have this out of the way, we can now define a concrete DAO class. We'll have a simple UserDao for testing.

package com.techtots.services.api.dao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import com.techtots.services.api.entity.User;
import com.techtots.services.common.vo.UserVo;

@Repository
public class UserDao extends BaseDao {

	public List<User> getList() {
		Criteria criteria = sessionFactory.getCurrentSession().createCriteria(User.class);
		
		return criteria.list();
	}
	
	public List<UserVo> list() {
		final List<UserVo> list = new ArrayList<UserVo>();
		
		getJdbcTemplate().query("select * from tbl_user", new RowMapper<Object>() {

			@Override
			public Object mapRow(ResultSet rs, int arg1) throws SQLException {
				UserVo vo = new UserVo();
				
				vo.setId(rs.getInt("id"));
				vo.setUsername(rs.getString("nickname"));
				
				list.add(vo);
				
				return null;
			}
			
		});
		
		return list;
	}
}


Since this is just a reference, I've implemented 2 different function to retrieve data. getList() uses Hibernate's SessionFactory while list() uses JdbcTemplate. Following classes are the UserVo and User entity classes which are used by the DAO respectively:

package com.techtots.services.common.vo;

public class UserVo {

	private int id;
	private String username;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

}

package com.techtots.services.api.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "tbl_user2")
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id", nullable = false)
	private int id;

	@Column(name = "name", nullable = false, length = 200)
	private String name;

	@Column(name = "email", nullable = false, length = 250, unique = true)
	private String email;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

}

To call this from your UserController, you'll need to add the @Transactional annotation at either the class or method level. You wouldn't usually add this at the controller level as it's better to have everything wrapped up in a service class.

Spring 3 MVC + Hibernate 4 + JdbcTemplate + Partial REST controller (Part 2)

Second part of the article will focus on the controllers which will process clients' requests as well as other helper classes.

We start off with a custom exception. We add on a HttpStatus code into the exception to return to clients:

package com.techtots.services.api.common.exceptions;

import org.springframework.http.HttpStatus;

public class RestException extends Exception {

 private static final long serialVersionUID = -6373811042517187537L;

 public static final HttpStatus DEFAULT_ERROR_STATUS = HttpStatus.BAD_REQUEST;
 
 private HttpStatus status;
  
 public HttpStatus getStatus() {
  return status;
 }

 public void setStatus(HttpStatus status) {
  this.status = status;
 }

 public RestException() {
  super();
  
  this.status = DEFAULT_ERROR_STATUS;
 }

 public RestException(String message) {
  super(message);
 
  this.status = DEFAULT_ERROR_STATUS;
 }
 
 public RestException(HttpStatus status, String message) {
  super(message);
  
  this.status = status;
 }
}

With Spring 3.2, we can use the @ControllerAdvice annotation to centralize validation and exception handling.

package com.techtots.services.api.common;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import com.techtots.services.api.common.exceptions.RestException;

@ControllerAdvice
public class RestResponseExceptionHandler extends ResponseEntityExceptionHandler {

 Logger log = LoggerFactory.getLogger(RestResponseExceptionHandler.class);
 
 @ExceptionHandler(RestException.class)
 protected ResponseEntity handleBadRequest(RestException ex) {

  log.error(ExceptionUtils.getStackTrace(ex));
  
  HttpHeaders headers = new HttpHeaders();
  headers.setContentType(MediaType.TEXT_HTML);
  
  ResponseEntity entity = new ResponseEntity(ex.getMessage(), headers, ex.getStatus()); 
  
  return entity;
 }
}


Finally, a test controller to hook everything up:

package com.techtots.services.api.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.techtots.services.api.common.exceptions.RestException;

@Controller
@RequestMapping("/auth")
public class AuthController extends BaseController {
 @RequestMapping(value = "/test", method = RequestMethod.GET, produces = "application/json")
 @ResponseBody
 public String test() throws Exception {

  return "ok";
 }

 @RequestMapping(value = "/test2", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
 @ResponseBody
 public void test2() throws Exception {

  throw new RestException("something's missing...");
 }

}

Calling the test method will return a JSON encoded message. While calling the test2 method will throw a HTTP exception with the message "something's missing" with HTTP error code 400 (the default HTTP error code returned in RestResponseExceptionHandler).

Spring 3 MVC + Hibernate 4 + JdbcTemplate + Partial REST controller (Part 1)

This is post serves as a reference point to quickly setup a Java web application with Spring 3 MVC, Hibernate 4, JdbcTemplate and a partial REST controller. We'll be consuming and responding JSON content via the REST controllers.

I'm using Eclipse as the IDE and will be connecting to MySQL database.

Let's start off with the libraries:

  • antlr-2.7.7.jar
  • aopalliance.jar
  • bonecp-0.7.1.RELEASE.jar
  • commons-lang3-3.1.jar
  • commons-logging-1.1.3.jar
  • dom4j-1.6.1.jar
  • guava-14.0.1.jar
  • hibernate-commons-annotations-4.0.2.Final.jar
  • hibernate-core-4.2.4.Final.jar
  • hibernate-jpa-2.0-api-1.0.1.Final.jar
  • jackson-annotations-2.2.0.jar
  • jackson-core-2.2.0.jar
  • jackson-databind-2.2.0.jar
  • javassist-3.15.0-GA.jar
  • jboss-logging-3.1.0.GA.jar
  • jboss-transaction-api_1.1_spec-1.0.1.Final.jar
  • mysql-connector-java-5.1.26-bin.jar
  • slf4j-api-1.7.5.jar
  • slf4j-simple-1.7.5.jar
  • spring-aop-3.2.3.RELEASE.jar
  • spring-beans-3.2.3.RELEASE.jar
  • spring-context-3.2.3.RELEASE.jar
  • spring-core-3.2.3.RELEASE.jar
  • spring-expression-3.2.3.RELEASE.jar
  • spring-jdbc-3.2.3.RELEASE.jar
  • spring-orm-3.2.3.RELEASE.jar
  • spring-tx-3.2.3.RELEASE.jar
  • spring-web-3.2.3.RELEASE.jar
  • spring-webmvc-3.2.3.RELEASE.jar

We'll continue to configure the web app by specifying the web.xml:





  
    contextConfigLocation    /WEB-INF/*ctx.xml  

  
    org.springframework.web.context.ContextLoaderListener
  

  
    api
    org.springframework.web.servlet.DispatcherServlet
    1
  

  
    api
    /api/*
  

  
    /errors/error.jsp
  



Next is to create the application context XML. We'll only have 3 bean definitions in the app-ctx:





 
  
  
  
  
  
  
  
  
  
  
  
  
 

 
  

  
    
      org.hibernate.dialect.MySQL5Dialect
      true
    
  
  
 
 
 
  
 
 



Now for the api-servlet.xml. No bean definition in this file as everything is annotation based.




 

 

 



We also have a single JSP to handle errors in errors/error.jsp. This is as bare as it gets:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




ERROR



<%
out.println(request.getAttribute("javax.servlet.error.message"));
out.println(request.getAttribute("javax.servlet.error.status_code"));

%>



This concludes basic setup for the webapp. Next part will include Java classes for the controller and error handling.

Thursday, August 1, 2013

Two things to get Kohana up and running

Here are 2 things to do right after meeting all requirements dictated by Kohana's install script:


  1. Set Cookie::$salt value in application/bootstrap.php:

    Cookie::$salt = 'my*salt*value*';
    

  2. Change base_url value in application/bootstrap.php to match the server configuration:

    Kohana::init(array(
     'base_url'   => '/my_app/',
    ));
    

    In this example, you should be calling http://myserver.com/my_app/