Tuesday, December 03, 2013

List of Real Spring-Based Projects

There are tons of Spring pet-projects on GitHub and it's cool. But, such big and powerful framework as Spring can't be lean by simple project. It requires big and complex samples which can highlight best practices and common approaches while using this framework. Here is the lift of such open sources projects:
  • Broadleaf Commerce - BroadleafCommerce is an open-source, eCommerce framework written entirely in Java
  • OpenLegacy - is a powerful, lightweight, extensible, and flexible Java-based API framework with tools for building applications and services over existing legacy applications…otherwise known as legacy modernization
  • Hippo CMS - is a Java based, Open Source Web Content Management platform. Hippo is created to help you deliver Personalized Experiences across a wide variety of Channels and Devices, around the globe, to increase conversion and to improve brand loyalty. 
  • Weceem CMS is a free, open source content management system. It is built with Grails, a rapid application development framework build on the Java VM using Groovy, Java and Spring.
  • Magnolia CMS (Community Edition) is a free, easy-to-use powerful Enterprise Content Management system. It is available under an Open Source license, the GPL version 3. Magnolia Community Edition includes an AJAX-powered intuitive web-browser interface, a clear Java programming API and a useful custom tag library for easy templating in JSP and Servlets. It can use any JSR-283 compliant content repository. 
  • Alfresco - is an open source enterprise content management repository (CMS) built by a team that includes the co-founder of Documentum. Its modular architecture uses the latest open src Java technologies. Includes Document Management, Records Management
  • JTalks.org is an open source project developed by JavaTalks community. The overall idea is to build a network of components like Forums, Articles, Webinars which are interconnected and they share the same database of users.
  • Spring by Example - self-explained
  • Greenhouse - Reference web application for Spring technologies and social destination for Spring developers
  • Zuul - is a free, open source web application which can be used to centralize and manage configuration for your internal applications. It enables your operations team to control changes and your developers a centralized place to organize settings.
  • LightAdmin - Pluggable data administration UI library for Java web applications
    References

    Monday, October 21, 2013

    REST with JAX-RS: Part 3 - Embedded Jetty

    See previous posts:
    There is one feature which I'd like to add. It's embedded Jetty server. I.e. we should be able to run our application from main() method.

    Update pom.xml, add jetty server as dependency
    ...
     <org.eclipse.jetty.version>9.0.6.v20130930</org.eclipse.jetty.version>
    ...
     
        org.eclipse.jetty
        jetty-webapp
        ${org.eclipse.jetty.version}
     
    ...
    
    Create special Launcher class which runs embedded Jetty Server. Jetty configuration registers port, host, base REST URL ("/api/*") and the most important - proper Spring config.
    package com.halyph;
    
    import com.halyph.config.AppConfig;
    import org.apache.cxf.transport.servlet.CXFServlet;
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.servlet.ServletContextHandler;
    import org.eclipse.jetty.servlet.ServletHolder;
    import org.springframework.web.context.ContextLoaderListener;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    
    
    public class Launcher {
    
        public static final int PORT = 8080;
    
        public static void main(final String[] args) throws Exception {
            Server server = new Server(PORT);
    
            // Register and map the dispatcher servlet
            final ServletHolder servletHolder = new ServletHolder(new CXFServlet());
            final ServletContextHandler context = new ServletContextHandler();
            context.setContextPath("/");
            context.addServlet(servletHolder, AppConfig.API_BASE);
            context.addEventListener(new ContextLoaderListener());
    
            context.setInitParameter("contextClass", AnnotationConfigWebApplicationContext.class.getName());
            context.setInitParameter("contextConfigLocation", AppConfig.class.getName());
    
            server.setHandler(context);
            server.start();
            server.join();
        }
    }
    
    
    

       You can find sources on GitHub

    References
    1. Going REST: embedding Jetty with Spring and JAX-RS (Apache CXF)
    2. Embedding Jetty or Tomcat in your Java Application

    Sunday, October 20, 2013

    REST with JAX-RS: Part 2 - Spring Java Config and CXF Improvement

    See previous post:
    Sample application from part 1 has several issues:
    1. It doesn't have application wide REST exception handler. This handler should catch and wrap all internal exceptions and present in some "standard" JSON format
    2. There are no autowiring for REST Resources and JAX-RS providers. I.e. we shouldn't declare REST service beans/providers manually in AppConfig (see part1)
    3. CXF object mapper (Jackson) should be configured and registered in CXF somehow
    Let's try to fix all these issues.

    JAX-RS has special approach for exception handling - ExceptionMapper.
    Let's define two mappers:
    • GeneralExceptionMapper - will catch and handle all Exceptions
    • NotFoundExceptionMapper - will catch and handle only NotFoundException
     
    package com.halyph.rest.provider;
    
    import javax.ws.rs.NotFoundException;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Response;
    import javax.ws.rs.ext.ExceptionMapper;
    import javax.ws.rs.ext.Provider;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    @Provider
    @Produces(MediaType.APPLICATION_JSON)
    public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
        /**
         * Map an exception to a {@link javax.ws.rs.core.Response}.
         *
         * @param exception the exception to map to a response.
         * @return a response mapped from the supplied exception.
         */
        @Override
        public Response toResponse(final NotFoundException exception) {
            Map<String, Object> info = new HashMap<>();
            info.put("msg", exception.getMessage());
            info.put("date", new Date());
            info.put("details", "The requested resource hasn't been found");
    
            return Response
                    .status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(info)
                    .type(MediaType.APPLICATION_JSON)
                    .build();
        }
    }
    
     
    package com.halyph.rest.provider;
    
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Response;
    import javax.ws.rs.ext.ExceptionMapper;
    import javax.ws.rs.ext.Provider;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    @Provider
    @Produces(MediaType.APPLICATION_JSON)
    public class GeneralExceptionMapper implements ExceptionMapper<exception> {
        /**
         * Map an exception to a {@link javax.ws.rs.core.Response}.
         *
         * @param exception the exception to map to a response.
         * @return a response mapped from the supplied exception.
         */
        @Override
        public Response toResponse(final Exception exception) {
            Map<String, Object> info = new HashMap<>();
            info.put("msg", exception.getMessage());
            info.put("date", new Date());
    
            return Response
                    .status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(info)
                    .type(MediaType.APPLICATION_JSON)
                    .build();
        }
    }
    

    And, modify UserResource which throws NotFoundException when some user can't be found by specified id
     
    @RestService
    @Path("/users")
    @Produces({MediaType.APPLICATION_JSON})
    @Consumes({MediaType.APPLICATION_JSON})
    public class UserResource {
    
     ...
        @GET
        @Path("/{id}")
        public User getUser(@PathParam("id") Integer id) {
            User user = service.getUser(id);
            if (user == null) {
                throw new NotFoundException();
            } else {
                return user;
            }
        }
    
        ...
    }
    

    Now, we have to implement REST resource/provider autowiring. 1st we create custom @RestService annotation.
     
    package com.halyph.util.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    public @interface RestService {
    }
    

    Now we have to implement Spring bean scanners which scan specified package and register "selected" beans in Spring context.

     
    package com.halyph.util;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
    import org.springframework.context.support.GenericApplicationContext;
    import org.springframework.core.type.filter.AnnotationTypeFilter;
    
    import javax.ws.rs.ext.Provider;
    import java.util.ArrayList;
    import java.util.List;
    
    public final class RestProviderBeanScanner {
    
        private RestProviderBeanScanner() { }
        public static List<Object> scan(ApplicationContext applicationContext, String... basePackages) {
            GenericApplicationContext genericAppContext = new GenericApplicationContext();
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(genericAppContext, false);
    
            scanner.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
            scanner.scan(basePackages);
            genericAppContext.setParent(applicationContext);
            genericAppContext.refresh();
    
            return new ArrayList<>(genericAppContext.getBeansWithAnnotation(Provider.class).values());
        }
    }
    
     
    package com.halyph.util;
    
    import com.halyph.util.annotation.RestService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
    import org.springframework.context.support.GenericApplicationContext;
    import org.springframework.core.type.filter.AnnotationTypeFilter;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public final class RestServiceBeanScanner {
    
        private RestServiceBeanScanner() { }
    
        public static List<Object> scan(ApplicationContext applicationContext, String... basePackages) {
            GenericApplicationContext genericAppContext = new GenericApplicationContext();
            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(genericAppContext, false);
    
            scanner.addIncludeFilter(new AnnotationTypeFilter(RestService.class));
            scanner.scan(basePackages);
            genericAppContext.setParent(applicationContext);
            genericAppContext.refresh();
    
            List<Object> restResources = new ArrayList<>(genericAppContext.getBeansWithAnnotation(RestService.class).values());
    
            return restResources;
        }
    }
    These two classes (scanner) RestServiceBeanScanner and RestProviderBeanScanner are almost identical and should be refactored to support generic scanner type. Let's left this for home work.

    There is additional issue with missed Object Mapper configuration:
     
    package com.halyph.rest.provider;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    
    import javax.ws.rs.ext.ContextResolver;
    import javax.ws.rs.ext.Provider;
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.TimeZone;
    
    @Provider
    public class ObjectMapperProvider implements ContextResolver<Objectmapper> {
    
        final ObjectMapper objectMapper;
    
        public ObjectMapperProvider() {
            this.objectMapper = new ObjectMapper();
            this.objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
    
            //set up ISO 8601 date/time stamp format:
            final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:sss'Z'");
            df.setTimeZone(TimeZone.getTimeZone("UTC"));
            this.objectMapper.setDateFormat(df);
        }
    
        @Override
        public ObjectMapper getContext(Class type) {
            return this.objectMapper;
        }
    }
    

    We have to update AppConfig:
    •  Add @ComponentScan to register services
    • Call RestProviderBeanScanner to register providers: json provider, ExceptionMapper and  ObjectMapperProvider
    • Call RestServiceBeanScanner to register REST services marked with @RestService annotation
     
    package com.halyph.config;
    
    import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
    import com.halyph.util.RestProviderBeanScanner;
    import com.halyph.util.RestServiceBeanScanner;
    import org.apache.cxf.bus.spring.SpringBus;
    import org.apache.cxf.endpoint.Server;
    import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;
    import javax.ws.rs.ext.RuntimeDelegate;
    import java.util.List;
    
    @Configuration
    @ComponentScan(AppConfig.SERVICE_PACKAGE)
    public class AppConfig {
    
        public static final String BASE_PACKAGE = "com.halyph";
        public static final String SERVICE_PACKAGE = BASE_PACKAGE + ".service";
        private static final String RESOURCES_PACKAGE = BASE_PACKAGE + ".rest";
        private static final String PROVIDER_PACKAGE = BASE_PACKAGE + ".rest.provider";
    
        @ApplicationPath("/")
        public class JaxRsApiApplication extends Application { }
    
        @Bean(destroyMethod = "shutdown")
        public SpringBus cxf() {
            return new SpringBus();
        }
    
        @Bean
        @DependsOn("cxf")
        public Server jaxRsServer(ApplicationContext appContext) {
            JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint(jaxRsApiApplication(), JAXRSServerFactoryBean.class);
            factory.setServiceBeans(restServiceList(appContext));
            factory.setAddress("/" + factory.getAddress());
            factory.setProviders(restProviderList(appContext, jsonProvider()));
            return factory.create();
        }
    
        @Bean
        public JaxRsApiApplication jaxRsApiApplication() {
            return new JaxRsApiApplication();
        }
    
        @Bean
        public JacksonJsonProvider jsonProvider() {
            return new JacksonJsonProvider();
        }
    
        private List<Object> restServiceList(ApplicationContext appContext) {
            return RestServiceBeanScanner.scan(appContext, AppConfig.RESOURCES_PACKAGE);
        }
    
        private List<Object> restProviderList(final ApplicationContext appContext,
                                              final JacksonJsonProvider jsonProvider) {
            final List<Object> providers = RestProviderBeanScanner.scan(appContext, PROVIDER_PACKAGE);
            providers.add(jsonProvider);
            return providers;
        }
    
    }
    

    Now, we should test this. 1st run application:
    mvn clean tomcat7:run

    Verify REST API calls:
    # pretty printed JSON, see ObjectMapperProvider 
    $  curl http://localhost:8080/api/users
    [ {
      "id" : 1,
      "name" : "foo"
    }, {
      "id" : 2,
      "name" : "bar"
    }, {
      "id" : 3,
      "name" : "baz"
    } ]
    
    # try to get non-existent user, expected to get NotFoundException JSON
    $ curl http://localhost:8080/api/users/100
    {
      "details" : "The requested resource hasn't been found",
      "date" : "2013-10-19T13:39:034Z",
      "msg" : null
    }
    
    # try to get GeneralException JSON
    $  curl http://localhost:8080/api/exception
    {
      "date" : "2013-10-19T13:40:049Z",
      "msg" : "generateException from ExceptionResource"
    }
    

       You can find sources on GitHub

    References
    1. Apache CXF exception handler for jaxrs (REST)
    2. Official documentation: Apache CXF Exception handling

    Saturday, October 19, 2013

    REST with JAX-RS: Part 1 - Spring Java Config

    There are many Java REST frameworks. We can devide these frameworks on three groups:
    1. Spring MVC. It can be used to implement RESTful services. This framework has been widly used, mature, etc. But, Spring, in general, does not support JAX-RS standard.
    2. JAX-RS implementation. I know at least four frameworks:
    3. Non-Standard. I.e. frameworks which do not support JAX-RS, or addition many other features. Please note, it assume that Spring MVC can be called "standard" ;-)
      • Dropwizard very cool frameworks. It supports not only JAX-RS. 
      • RESTX, lightweight framework.
    It's logically to ask yourself why don't use Spring MVC for REST services development. There is a  very good article on InfoQ: A Comparison of Spring MVC and JAX-RS. I consider to use JAX-RS frameworks for REST API and Spring MVC for everything else . The most popular are Apache CXF  and Jersey. Also, Apache CXF has SOAP services support. Actually, you can easily switch between JAX-RS frameworks till you use standard approaches.

    Let's create simple Spring JAX-RS application with Spring Java Configs (see sample application based on Spring context xml [3])

    Create pom.xml file
    
        4.0.0
        jaxrs-tutorials
        jaxrs-tutorials
        war
        1.0-SNAPSHOT
        jaxrs-tutorials Maven Webapp
        http://maven.apache.org
    
        
            UTF-8
            1.7
    
            2.7.6
            3.2.3.RELEASE
            1.7.5
            1.0.13
            3.0.1
            2.0.2
    
            3.0
            2.0
            2.2
            2.6
        
    
        
    
            
                org.apache.cxf
                cxf-rt-frontend-jaxrs
                ${cxf.version}
            
    
            
            
                org.springframework
                spring-context
                ${org.springframework-version}
                
                    
                    
                        commons-logging
                        commons-logging
                    
                
            
            
                org.springframework
                spring-webmvc
                ${org.springframework-version}
            
    
            
            
                org.slf4j
                slf4j-api
                ${org.slf4j-version}
            
            
                org.slf4j
                jcl-over-slf4j
                ${org.slf4j-version}
                runtime
            
            
                ch.qos.logback
                logback-classic
                ${ch.qos.logback-version}
            
    
            
            
                javax.servlet
                javax.servlet-api
                ${servlet-version}
                provided
            
    
            
            
                com.fasterxml.jackson.jaxrs
                jackson-jaxrs-json-provider
                ${jackson-version}
            
    
        
    
        
    
            jaxrs-tutorials
    
            
                
                    org.apache.tomcat.maven
                    tomcat7-maven-plugin
                    2.0
                    
                        /
                        8080
                    
                
                
                    org.apache.maven.plugins
                    maven-compiler-plugin
                    ${maven-compiler-plugin-version}
                    
                        ${java-version}
                        ${java-version}
                    
                
            
        
    
    
    Create some sample entity
    package com.halyph.entity;
    
    public class User {
    
        private Integer id;
        private String name;
    
        public User() {
        }
    
        public User(Integer id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        @Override
        public String toString() {
            return String.format("{id=%s,name=%s}", id, name);
        }
    }
    
    We should implement Service(s) which manages this entity:
    package com.halyph.service;
    
    import com.halyph.entity.User;
    
    import javax.ws.rs.core.Response;
    import java.util.Collection;
    
    public interface UserService {
    
        Collection<User> getUsers();
    
        User getUser(Integer id);
    
        Response add(User user);
    
    }
    
    package com.halyph.service;
    
    import com.halyph.entity.User;
    import org.springframework.stereotype.Service;
    
    import javax.ws.rs.core.Response;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
         private static Map<Integer, User> users = new HashMap<Integer, User>();
    
        static {
            users.put(1, new User(1, "foo"));
            users.put(2, new User(2, "bar"));
            users.put(3, new User(3, "baz"));
        }
    
        public UserServiceImpl() {
        }
    
        @Override
        public Collection<User> getUsers() {
            return users.values();
        }
    
        @Override
        public User getUser(Integer id) {
            return users.get(id);
        }
    
        @Override
        public Response add(User user) {
            user.setId(users.size()+1);
            users.put(user.getId(), user);
    
            //do more stuff to add user to the system..
            return Response.status(Response.Status.OK).build();
        }
    
    }
    
    It's time to introduce REST services. with the next endpoints /api/users and /api/exception. So, we  have bunch of REST URIs:
    • GET /api/users - get all users
    • GET /api/users/{id} - get user with id
    • POST /api/users - accept "user" json and create the specified user on back-end
    • GET /api/exception - throw exception
     
    package com.halyph.rest;
    
    import com.halyph.entity.User;
    import com.halyph.service.UserService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import javax.ws.rs.Consumes;
    import javax.ws.rs.GET;
    import javax.ws.rs.POST;
    import javax.ws.rs.Path;
    import javax.ws.rs.PathParam;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Response;
    import java.util.Collection;
    
    @Path("/users")
    @Produces({MediaType.APPLICATION_JSON})
    @Consumes({MediaType.APPLICATION_JSON})
    public class UserResource {
    
        private static Logger log = LoggerFactory.getLogger(UserResource.class);
    
        @Autowired
        UserService service;
    
        public UserResource() {
        }
    
        @GET
        public Collection<User> getUsers() {
            return service.getUsers();
        }
    
        @GET
        @Path("/{id}")
        public User getUser(@PathParam("id") Integer id) {
            return service.getUser(id);
        }
    
        @POST
        public Response add(User user) {
            log.info("Adding user {}", user.getName());
            service.add(user);
            return Response.status(Response.Status.OK).build();
        }
    }
    
    Also we added /api/exception REST url to demonstrate how CXF deals with exceptions:
    package com.halyph.rest;
    
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    
    @Path("/exception")
    public class ExceptionResource {
    
        public ExceptionResource() { }
    
        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String generateException() throws Exception {
            throw new Exception("generateException from ExceptionResource");
        }
    }
    
    
    So, what's left? In general we are creating some web.xml where we configure Apache CXF, etc. But, we will use Spring feature and implement all our configuration in Spring Java Configs.
    Our web.xml willl be empty, some App Servers still require it:
    
    
    Next, we should create some class which does the same work which had been done by web.xml:
    package com.halyph.config;
    
    import org.apache.cxf.transport.servlet.CXFServlet;
    import org.springframework.web.WebApplicationInitializer;
    import org.springframework.web.context.ContextLoaderListener;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRegistration;
    import java.util.Set;
    
    public class WebAppInitializer implements WebApplicationInitializer {
    
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            servletContext.addListener(new ContextLoaderListener(createWebAppContext()));
            addApacheCxfServlet(servletContext);
        }
    
        private void addApacheCxfServlet(ServletContext servletContext) {
            CXFServlet cxfServlet = new CXFServlet();
    
            ServletRegistration.Dynamic appServlet = servletContext.addServlet("CXFServlet", cxfServlet);
            appServlet.setLoadOnStartup(1);
    
            Set<String> mappingConflicts = appServlet.addMapping("/api/*");
        }
    
        private WebApplicationContext createWebAppContext() {
            AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
            appContext.register(AppConfig.class);
            return appContext;
        }
    
    }
    
    So, "web.xml" is configured, now we should configure Spring context:
    package com.halyph.config;
    
    import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
    import com.halyph.rest.UserResource;
    import org.apache.cxf.bus.spring.SpringBus;
    import org.apache.cxf.endpoint.Server;
    import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
    import com.halyph.rest.ExceptionResource;
    import com.halyph.service.UserService;
    import com.halyph.service.UserServiceImpl;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;
    import javax.ws.rs.ext.RuntimeDelegate;
    import java.util.Arrays;
    
    @Configuration
    public class AppConfig {
    
        @ApplicationPath("/")
        public class JaxRsApiApplication extends Application { }
    
        @Bean(destroyMethod = "shutdown")
        public SpringBus cxf() {
            return new SpringBus();
        }
    
        @Bean
        @DependsOn("cxf")
        public Server jaxRsServer(ApplicationContext appContext) {
            JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint(jaxRsApiApplication(), JAXRSServerFactoryBean.class);
            factory.setServiceBeans(Arrays.<Object>asList(userResource(), exceptionResource()));
            factory.setAddress("/" + factory.getAddress());
            factory.setProvider(jsonProvider());
            return factory.create();
        }
    
        @Bean
        public JaxRsApiApplication jaxRsApiApplication() {
            return new JaxRsApiApplication();
        }
    
        @Bean
        public JacksonJsonProvider jsonProvider() {
            return new JacksonJsonProvider();
        }
    
        @Bean
        public UserService userService() {
            return new UserServiceImpl();
        }
    
        @Bean
        public UserResource userResource() {
            return new UserResource();
        }
    
        @Bean
        public ExceptionResource exceptionResource() {
            return new ExceptionResource();
        }
    }
    
    Please note, how service and rest beans have been registered, also we added jackson provider which serialize bean in JSON format.

    We almost done, now we should verify out work. Run application:
    mvn clean tomcat7:run

    Test REST API:
    curl http://localhost:8080/api/users
    curl http://localhost:8080/api/users/1
    curl -v http://localhost:8080/api/exception
    curl http://localhost:8080/api/users -X POST -H "Content-Type: application/json" -d '{"name":"John"}'
    curl http://localhost:8080/api/users
    

    After the last call you should get four users from back-end.
    $ curl http://localhost:8080/api/users
    [{"id":1,"name":"foo"},{"id":2,"name":"bar"},{"id":3,"name":"baz"}]
    
    $ curl http://localhost:8080/api/users -X POST -H "Content-Type: application/json" -d '{"name":"John"}'
    
    $  curl http://localhost:8080/api/users
    [{"id":1,"name":"foo"},{"id":2,"name":"bar"},{"id":3,"name":"baz"},{"id":4,"name":"John"}]
    

       You can find sources on GitHub.

    References
    1. InfoQ: A Comparison of Spring MVC and JAX-RS
    2. REST client, CXF server : JAX-RS or SPRING-MVC ?
    3. REST web services with JAX-RS, Apache CXF and Spring Security
    4. Official Apache CXF Doc: Configuring JAX-RS services in container with Spring configuration file.
    5. Converting Jersey REST Examples to Apache CXF

    Thursday, October 03, 2013

    Java Object to Object Mapper

    I've been using Dozer some time on the project. But, recently I have got very interesting error which motivates me to look around and try to use other Object to Object mapper.

    Here is the list of tools I've found:
    • Dozer: Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another. Typically, these Java Beans will be of different complex types.
    • Orika: Orika is a Java Bean mapping framework that recursively copies (among other capabilities) data from one object to another. It can be very useful when developing multi-layered applications.
    • Transmorph: Transmorph is a free java library used to convert a Java object of one type into an object of another type (with another signature, possibly parameterized).
    • EZMorph: EZMorph is simple java library for transforming an Object to another Object. It supports transformations for primitives and Objects, for multidimensional arrays and transformations with DynaBeans
    • Commons-BeanUtils: ConvertUtils -> Utility methods for converting String scalar values to objects of the specified Class, String arrays to arrays of the specified Class.
    • Commons-Lang: ArrayUtils -> Operations on arrays, primitive arrays (like int[]) and primitive wrapper arrays (like Integer[]).
    • Commons-Convert: Commons-Convert aims to provide a single library dedicated to the task of converting an object of one type to another. The first stage will focus on Object to String and String to Object conversions.
    • Morph: Morph is a Java framework that eases the internal interoperability of an application. As information flows through an application, it undergoes multiple transformations. Morph provides a standard way to implement these transformations.
    • Lorentz: Lorentz is a generic object-to-object conversion framework. It provides a simple API to convert a Java objects of one type into an object of another type. (seems dead)
    • Spring framework: Spring has an excellent support for PropertyEditors, that can also be used to transform Objects to/from Strings.
    • ModelMapper: ModelMapper is an intelligent object mapping framework that automatically maps objects to each other. It uses a convention based approach to map objects while providing a simple refactoring safe API for handling specific use cases.
    • OTOM: With OTOM, you can copy any data from any object to any other object. The possibilities are endless. Welcome to "Autumn".
    • Smooks: The Smooks JavaBean Cartridge allows you to create and populate Java objects from your message data (i.e. bind data to).
    • Nomin: Nomin is a mapping engine for the Java platform transforming object trees according to declarative mapping rules. This Java mapping framework aims to reduce efforts when it's needed to map different structures to each other.
    • Modelbridge: Modelbridge is an Eclipse plugin that lets you copy data between Java objects.
    • omapper: This project aims at providing a simple library to ease the process of mapping objects or beans from one design layer to another design layer, using annotations. One can specify mappings both in source class (Sink Annotation) and target class(Source Annotation). Supports mapping composed user class objects and array fields. 
    • Moo: Moo maps an object or graph of objects to another object or set of objects while trying to stay as unintrusive as possible and easy-to-use. Moo makes it possible to create quick copies and data transfer objects.   
    • OpenL Tablets: treats tables in Excel and Word files as a source of application logic. This approach may be unusual, but it has it's own unique advantages, in particular it allows to close the gap between business world and IT world.
    • JMapper: JMapper Framework is a java bean to java bean mapper, allows you to perform the passage of data dynamically with annotations and / or XML.
    References

    Tuesday, August 27, 2013

    How to remove .svn folders recursively?


    Validate the list of folders which will be removed:
    $ find . -name .svn -exec ls {} \;
    

    Run recursive remove:
    $ find . -name .svn -exec rm -rf {} \;
    
    This script is run from the current folder.  Be sure you've specified find -name properly.


    References:

    Wednesday, August 21, 2013

    How to Install Git 1.8.3.4 on CentOS 6.4

    1. Check the CentOS version
    2. $ cat /etc/redhat-release
      CentOS release 6.4 (Final)
      
    3. Check git version
    4. $ git --version
      git version 1.7.1
      
    5. Enable/disable yum proxy settings (find/add "proxy" string with related proxy server)
      $ vim /etc/yum.conf
      
    6. Try to update git via yum package manager. But you will get the message that git is up to date
    7. $ sudo yum update git 
      
    8. Based on instruction from Getting Started Installing Git install git from sources:
    9. $ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc
      $ wget https://www.kernel.org/pub/software/scm/git/git-1.8.3.4.tar.gz
      $ tar xzvf git-1.8.3.4.tar.gz
      $ cd git-1.8.3.4
      $ sudo make prefix=/usr/local all
      $ sudo make prefix=/usr/local install
      
    10. Re-run bash shell and verify the current git version
    11. $ git --version
      git version 1.8.3.4
      
    References:

    Saturday, August 17, 2013

    Git, Jenkins, Gerrit, Code Review and pre-tested commits


    This post is a note to myself. There are bunch of tools on the market which can be successfully used in yours development process. But, I'm interested in gitJenkins only. Also, git/jenkins supports tons on different workflows, but here I'd like to talk about pre-tested commits.

    So, what does it mean "pre-tested commit"? Initially this type of commits was introduced in TeamCity CI Server:
    Typically, your team submits code to Version Control BEFORE verifying that it works, and risks breaking the build every single time — causing more problems than necessary. [2]

    But, it's TeamCity and we need similar functionality in Jenkins.

    There are several ways to get it:
    • Implement Jenkins plugin which can work with integration branches and simulate pre-tested workflow
    • Reuse existing tool like Gerrit
    But you say Gerrit is a code review tool. And you are right. Fortunately, Gerrit support pre-tested commit in his code review workflows (see source [3]):

    Gerrit Code Review represents a step-forward in the way the team work on the code and share idea and the ownership of the design and coding decisions.  (see [4])
     Tools required to support this workflow:

     To see all these in action I've added two videos which demonstrate this concept:


    Also, it worth to review this slides. Here is nicely demonstrated why Gerrit is cool and why un-tested commit is bad.



    References:
    1. Pretested commits – why does it matter to us?
    2. Pre-Tested Commit: No broken code in your version control. Ever.
    3. TeamForge Git /Gerrit Integration with Jenkins CI
    4. Jenkins CI and Gerrit Code Review dance together
    5. Jeknins - Designing pre-tested commit
    6. Open Stack - Gerrit Workflow
    7. Open Stack - Gerrit Jenkins Github

    Tuesday, August 06, 2013

    Typical .gitignore for Java Projects


    I'm sure that every developer have some basic (skeleton) .gitignore file which is coping from project to project :-)

    As a Java developer I'd like to talk about typical .gitignore file which can be added at the beginning the project (a la initial commit).

    I've been using Github collection of useful .gitignore templates. It's very useful repo contains tons of standard .gitignores. General workflow is to assemble my .gitignore based on several templates:
    But recently I've found very nice service gitignore.io which can generate nicely assembled .gitignore based on provided criteria:

    Here is the generated file:
     
    # Generated by http://gitignore.io
    
    ### Java ###
    *.class
    
    # Package Files #
    *.jar
    *.war
    *.ear
    
    ### IntelliJ ###
    *.iml
    *.ipr
    *.iws
    .idea/
    
    ### Eclipse ###
    *.pydevproject
    .project
    .metadata
    bin/**
    tmp/**
    tmp/**/*
    *.tmp
    *.bak
    *.swp
    *~.nib
    local.properties
    .classpath
    .settings/
    .loadpath
    
    # External tool builders
    .externalToolBuilders/
    
    # Locally stored "Eclipse launch configurations"
    *.launch
    
    # CDT-specific
    .cproject
    
    # PDT-specific
    .buildpath
    
    ### Maven ###
    target/
    
    
    Very convenient. Have a try.

    Thursday, July 04, 2013

    The Modern Java Web Developer


    I've decided to make some notes regarding Matt Raible presentations:


    Now, I restrict my language set to: Java and JavaScript. There are huge hype around Scala/Play (was Ruby/Rails).
    This decision is based on the local job market.

    So, the modern Java Web developer should have skills in the next areas:
    • Concepts:
      • High performance web site
      • Security consideration
      • Web for Mobile devices
      • Cloud base deployment
      • Caching
      • Page Speed
      • RESTful Architecture
      • Clustering
    • JavaScript, HTML5, CSS3, LESS
    • jQuery, jQuery UI
    • Twitter Bootstrap
    Avoid languages which compile to JavaScript:
    • CoffeeScript
    • TypeScript
    • Dart

    Tuesday, July 02, 2013

    Concurrency vs. Parallelism Definitions

    Recently I had long discussion in scope of  "concurrency" and "parallelism" definitions. That's why I've decided to pick the most trusted sources for these definitions in one place:

    From Akka documentation:
    Concurrency and parallelism are related concepts, but there are small differences. Concurrency means that two or more tasks are making progress even though they might not be executing simultaneously. This can for example be realized with time slicing where parts of tasks are executed sequentially and mixed with parts of other tasks. Parallelism on the other hand arise when the execution can be truly simultaneous.

    From Oracle "Multithreaded Programming Guide":
    Parallelism - A condition that arises when at least two threads are executing simultaneously.
    Concurrency - A condition that exists when at least two threads are making progress. A more generalized form of parallelism that can include time-slicing as a form of virtual parallelism.

    Monday, July 01, 2013

    JAI and Image Processing in Java

     

    I have minimal experience with Image Processing and when I'm talking about it I mean:
    • read/write different image formats (jpg, png, tiff, ice, etc.)
    • flip, rotate image
    • crop and clip image by defined path
    • convert image's color (e.g. RGB to gray scale)
    • reduce image size
    • find edges
    • image histograms and color counting
    • different operations (e.g. AND, OR and XOR)
    • read image meta info
    Of course, I don't want to waist time implementing all these by myself, because it's basic operation in Image Processing (IP) domain. And I believe that all these tasks are pretty much trivial for any engineer who's working in Image Processing.

    As usual I have strict restriction regarding the licence of selected library (which can be used in any commecrial product).

    So, I've started googling. Here is the initial (and filtered) results:
    JAI
    This library was developed by SUN. Unfortunately, it's not supported anymore. But, it's very powerful and has nice facilities.
    JAI is available for Windows, Linux, Mac OS X, and Solaris, and takes advantage of native acceleration when available. However, you can use the JAI libraries on any platform if you add the following JAR files to your classpath: 
    JAI Tutorials, Guides, Sources
    Also, you should check the JAI sources. Here is the master JAI project on java.net with several sub-projects:
    JAI Licenses. It's very tricky question. Here is the quote from java.net:
    As far as I know, the libraries from JAI and JAI Image I/O Tools can be
    bundled with a commercial product or can be downloaded on the fly using
    the Java Web Start technology.

    You might like to take a look at how people have been using JAI and JAI
    Image I/O Tools:

    http://java.sun.com/products/java-media/jai/success/
    As for commercial license, you might like to read the LICENSE and README
    files for the binaries:

    http://download.java.net/media/jai/builds/release/1_1_3/LICENSE-jai.txt
    http://download.java.net/media/jai/builds/release/1_1_3/DISTRIBUTIONREAD...
    http://download.java.net/media/jai-imageio/builds/release/1.1/LICENSE-ja...
    http://download.java.net/media/jai-imageio/builds/release/1.1/DISTRIBUTI...

    and the licenses for the source code (if you make your own build of JAI):
    https://jai.dev.java.net/#Licenses
    https://jai-imageio.dev.java.net/index.html#Licenses
    I'm not sure whether you can include this library into your commercial project. You should answer on this question by yourself. 

    ImageJ 
    It's a very popular application/library in Java community. I've considered it also. This library has very big community. Also, it has many plugins. Documentation (User Guide, tutorials, wiki, FAQ, etc) are located in one place, easy to find. But, I wouldn't use ImageJ in case of simple image processing operations. 

    Other Libraries
    I've reviewed the mentioned above other libraries and I must say they have nice features also. But they are too heavy (as well as ImageJ) for the tasks I've been trying to accomplish.

    Summary
    • JAI isn't maintained by Oracle. You should be careful when including it in commercial product
    • ImageJ, Commons Imaging, JAITools , and Marvin are providing huge set of IP facilities, but it might be redundant in your case.
    • Plain Java core image library can solve almost all your needs, except TIFF/PDF file  processing.
    • TIFF/PDF file processing should be done by 3d-party libraries (e.g. iText, JAI).
    References:

    Tuesday, June 25, 2013

    How to install the latest Git on OS X

                

    I've been using git v.1.7.10.2 long time, despite the fact that git v.1.8+ released several months ago. So, I've decided to updated my local git via homebrew.

    $ git --version
    git version 1.7.10.2 (Apple Git-33)
    
    $ brew install git
    
    ==> Downloading http://git-core.googlecode.com/files/git-1.8.3.1.tar.gz
    ######################################################################## 100.0%
    ==> make prefix=/usr/local/Cellar/git/1.8.3.1 CC=cc CFLAGS= LDFLAGS= install
    ==> make CC=cc CFLAGS= LDFLAGS=
    ==> make clean
    ==> make CC=cc CFLAGS= LDFLAGS=
    ==> Downloading http://git-core.googlecode.com/files/git-manpages-1.8.3.1.tar.gz
    ######################################################################## 100.0%
    ==> Downloading http://git-core.googlecode.com/files/git-htmldocs-1.8.3.1.tar.gz
    ######################################################################## 100.0%
    ==> Caveats
    The OS X keychain credential helper has been installed to:
      /usr/local/bin/git-credential-osxkeychain
    
    The 'contrib' directory has been installed to:
      /usr/local/share/git-core/contrib
    
    Bash completion has been installed to:
      /usr/local/etc/bash_completion.d
    
    zsh completion has been installed to:
      /usr/local/share/zsh/site-functions
    ==> Summary
      /usr/local/Cellar/git/1.8.3.1: 1324 files, 28M, built in 113 seconds
    
    $ git --version
    git version 1.7.10.2 (Apple Git-33)
    
    As you can see the latest git isn't on PATH. Than I've asked the doctor's help:
    $ brew doctor
    Warning: /usr/bin occurs before /usr/local/bin
    This means that system-provided programs will be used instead of those
    provided by Homebrew. The following tools exist at both paths:
    
        git
        git-cvsserver
        git-receive-pack
        git-shell
        git-upload-archive
        git-upload-pack
        gitk
    
    Consider amending your PATH so that /usr/local/bin
    occurs before /usr/bin in your PATH.
    
    I.e. I need to put the homebrew git before system git:
    $ echo 'export PATH=/usr/local/bin:$PATH' >> ~/.bash_profile
    After the terminal's restart I've got the next:
    $ git --version
    git version 1.8.3.1

    Friday, June 14, 2013

    Java Barcode Generation and Scanning Library

    Recently I had to pick library which can generate and scan different 2D barcodes. Actually I was interested in QR Code and Data Matrix. There is only ONE open source Java library which generates and scans barcode, it's Zxing. You might say there are others, but they cost money.

    I've been following Zxing some time and must admit that this library has very vibrant community, code base is clean, plenty of unit tests. Also, API is very clean. Here is a simple code snippet for scanning Data Matrix barcode:

    String filename = "file.png";
        
    Map<DecodeHintType,Object> hints = new EnumMap<DecodeHintType,Object>(DecodeHintType.class);
    hints.put(DecodeHintType.POSSIBLE_FORMATS, Arrays.asList(BarcodeFormat.DATA_MATRIX));
    hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
    BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(
            new BufferedImageLuminanceSource(
                ImageIO.read(new FileInputStream(filename)))));
    
    Result result = new MultiFormatReader().decode(binaryBitmap, hints);
    System.out.println(result.getText());
    

    I've tested Zxing v.2.2 QR Code/Data Matrix scanning. And Zxing implementation for QR Code scanning is more robust and well implemented in comparison to Data Matrix. I.e. if you have freedom and can choose any of these two barcodes, than select QR Code.

    Note, there are several open source barcode generation libraries and only a few barcode scanners (actually one Zxing for Java)

    Monday, May 27, 2013

    JEEConf - Kiev, 2013 - Report

    The third JEEConf was superb. It was well organized, with friendly atmosphere and high-quality speakers. And I had a dilemma what topics/speaker I should visit. :-) 

    So, I've selected the next speakers:
    Unfortunately, there were other topics which went in parallel. Now, I'm waiting on video from organizer.

    On the photo from left to right: 3) Venkat Subramaniam, 4) Mohamed Taman 5) me, 6) Jacek Laskowsk, 7) Reza Rahma and 8)Anton Keks.



    See:
    JEEConf - Kiev, 2012 - Report
    JEEConf - Kiev, 2011 - Report

    Thursday, May 23, 2013

    Graphics Unit Conversion

    Working with PDF files and with scanned images it always required to convert from point to millimeter, or from millimeter to pixel, and so on.

    There is very interesting quote from iText in Action book:
    What is the measurement unit in PDF documents? Most of the measurements in PDFs are expressed in user space units. ISO-32000-1 (section 8.3.2.3) tells us “the  default  for  the  size  of  the  unit  in  default  user  space  (1/72  inch)  is approximately  the  same  as  a  point  (pt),  a  unit  widely  used  in  the  printing industry. It is not exactly the same; there is no universal definition of a point.” In short, 1 in. = 25.4 mm = 72 user units (which roughly corresponds to 72 pt).

    Of course,  you can easily write these utilities by yourself. But, sometimes it makes sense to look around in your project, maybe you've already had all required utils.

    So, here are code snippets from several libraries

    1. Barcode4j

    package org.krysalis.barcode4j.tools;
    
    /**
     * Utility class for unit conversions.
     * 
     * @author Jeremias Maerki
     * @version $Id: UnitConv.java,v 1.2 2004/09/04 20:25:56 jmaerki Exp $
     */
    public class UnitConv {
    
        /**
         * Utility class: Constructor prevents instantiating when subclassed.
         */
        protected UnitConv() {
            throw new UnsupportedOperationException();
        }
        
        /**
         * Converts millimeters (mm) to points (pt)
         * @param mm the value in mm
         * @return the value in pt
         */
        public static double mm2pt(double mm) {
            return mm * 2.835;
        }
    
        /**
         * Converts points (pt) to millimeters (mm)
         * @param pt the value in pt
         * @return the value in mm
         */
        public static double pt2mm(double pt) {
            return pt / 2.835;
        }
        
        /**
         * Converts millimeters (mm) to inches (in)
         * @param mm the value in mm
         * @return the value in inches
         */
        public static double mm2in(double mm) {
            return mm / 25.4;
        }
        
        /**
         * Converts inches (in) to millimeters (mm)
         * @param in the value in inches
         * @return the value in mm
         */
        public static double in2mm(double in) {
            return in * 25.4;
        }
        
        /**
         * Converts millimeters (mm) to pixels (px)
         * @param mm the value in mm
         * @param resolution the resolution in dpi (dots per inch)
         * @return the value in pixels
         */
        public static int mm2px(double mm, int resolution) {
            return (int)Math.round(mm2in(mm) * resolution);
        }
    }
    
    2. iText v.4.2
    package com.lowagie.text;
    
    /**
     * A collection of convenience methods that were present in many different iText
     * classes.
     */
    
    public class Utilities {
    
    ...
     
     /**
      * Measurement conversion from millimeters to points.
      * @param value a value in millimeters
      * @return a value in points
      * @since 2.1.2
      */
     public static final float millimetersToPoints(float value) {
         return inchesToPoints(millimetersToInches(value));
     }
    
     /**
      * Measurement conversion from millimeters to inches.
      * @param value a value in millimeters
      * @return a value in inches
      * @since 2.1.2
      */
     public static final float millimetersToInches(float value) {
         return value / 25.4f;
     }
    
     /**
      * Measurement conversion from points to millimeters.
      * @param value a value in points
      * @return a value in millimeters
      * @since 2.1.2
      */
     public static final float pointsToMillimeters(float value) {
         return inchesToMillimeters(pointsToInches(value));
     }
    
     /**
      * Measurement conversion from points to inches.
      * @param value a value in points
      * @return a value in inches
      * @since 2.1.2
      */
     public static final float pointsToInches(float value) {
         return value / 72f;
     }
    
     /**
      * Measurement conversion from inches to millimeters.
      * @param value a value in inches
      * @return a value in millimeters
      * @since 2.1.2
      */
     public static final float inchesToMillimeters(float value) {
         return value * 25.4f;
     }
    
     /**
      * Measurement conversion from inches to points.
      * @param value a value in inches
      * @return a value in points
      * @since 2.1.2
      */
     public static final float inchesToPoints(float value) {
         return value * 72f;
     }
        ...
    }
    
    3. FOP
    package org.apache.fop.util;
    
    import java.awt.geom.AffineTransform;
    
    /**
     * Utility class for unit conversions.
     * @deprecated use org.apache.xmlgraphics.util.UnitConv instead.
     */
    public final class UnitConv {
    
        /**
         * conversion factory from millimeters to inches.
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.IN2MM instead.
         */
        public static final float IN2MM = org.apache.xmlgraphics.util.UnitConv.IN2MM;
    
        /**
         * conversion factory from centimeters to inches.
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.IN2CM instead.
         */
        public static final float IN2CM = org.apache.xmlgraphics.util.UnitConv.IN2CM;
    
        /**
         * conversion factory from inches to points.
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.IN2PT instead.
         */
        public static final int IN2PT = org.apache.xmlgraphics.util.UnitConv.IN2PT;
    
        /**
         * Converts millimeters (mm) to points (pt)
         * @param mm the value in mm
         * @return the value in pt
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.mm2pt(mm) instead.
         */
        public static double mm2pt(double mm) {
            return org.apache.xmlgraphics.util.UnitConv.mm2pt(mm);
        }
    
        /**
         * Converts millimeters (mm) to millipoints (mpt)
         * @param mm the value in mm
         * @return the value in mpt
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.mm2mpt(mm) instead.
         */
        public static double mm2mpt(double mm) {
            return org.apache.xmlgraphics.util.UnitConv.mm2mpt(mm);
        }
    
        /**
         * Converts points (pt) to millimeters (mm)
         * @param pt the value in pt
         * @return the value in mm
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.pt2mm(pt) instead.
         */
        public static double pt2mm(double pt) {
            return org.apache.xmlgraphics.util.UnitConv.pt2mm(pt);
        }
    
        /**
         * Converts millimeters (mm) to inches (in)
         * @param mm the value in mm
         * @return the value in inches
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.pt2mm(pt) instead.
         */
        public static double mm2in(double mm) {
            return org.apache.xmlgraphics.util.UnitConv.mm2in(mm);
        }
    
        /**
         * Converts inches (in) to millimeters (mm)
         * @param in the value in inches
         * @return the value in mm
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.in2mm(in) instead.
         */
        public static double in2mm(double in) {
            return org.apache.xmlgraphics.util.UnitConv.in2mm(in);
        }
    
        /**
         * Converts inches (in) to millipoints (mpt)
         * @param in the value in inches
         * @return the value in mpt
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.in2mpt(in) instead.
         */
        public static double in2mpt(double in) {
            return org.apache.xmlgraphics.util.UnitConv.in2mpt(in);
        }
    
        /**
         * Converts inches (in) to points (pt)
         * @param in the value in inches
         * @return the value in pt
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.in2pt(in) instead.
         */
        public static double in2pt(double in) {
            return org.apache.xmlgraphics.util.UnitConv.in2pt(in);
        }
    
        /**
         * Converts millipoints (mpt) to inches (in)
         * @param mpt the value in mpt
         * @return the value in inches
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.mpt2in(mpt) instead.
         */
        public static double mpt2in(double mpt) {
            return org.apache.xmlgraphics.util.UnitConv.mpt2in(mpt);
        }
    
        /**
         * Converts millimeters (mm) to pixels (px)
         * @param mm the value in mm
         * @param resolution the resolution in dpi (dots per inch)
         * @return the value in pixels
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.mm2px(mm, resolution) instead.
         */
        public static double mm2px(double mm, int resolution) {
            return org.apache.xmlgraphics.util.UnitConv.mm2px(mm, resolution);
        }
    
        /**
         * Converts millipoints (mpt) to pixels (px)
         * @param mpt the value in mpt
         * @param resolution the resolution in dpi (dots per inch)
         * @return the value in pixels
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.mpt2px(mpt, resolution) instead.
         */
        public static double mpt2px(double mpt, int resolution) {
            return org.apache.xmlgraphics.util.UnitConv.mpt2px(mpt, resolution);
        }
    
        /**
         * Converts a millipoint-based transformation matrix to points.
         * @param at a millipoint-based transformation matrix
         * @return a point-based transformation matrix
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.mptToPt(at) instead.
         */
        public static AffineTransform mptToPt(AffineTransform at) {
            return org.apache.xmlgraphics.util.UnitConv.mptToPt(at);
        }
    
        /**
         * Converts a point-based transformation matrix to millipoints.
         * @param at a point-based transformation matrix
         * @return a millipoint-based transformation matrix
         * @deprecated use org.apache.xmlgraphics.util.UnitConv.ptToMpt(at) instead.
         */
        public static AffineTransform ptToMpt(AffineTransform at) {
            return org.apache.xmlgraphics.util.UnitConv.ptToMpt(at);
        }
    }
    

    Monday, May 20, 2013

    Equals, HashCode and toString in Java

    You probably know that equals, hashCode and toString methods are very important in Java. And they must be carefully implemented. Joshua Bloch has written about this in his famous book "Effective Java (2nd Edition)", items 8 and 9.

    Let's create simple bean object and implement the mentioned above methods:
    package org.halyph;
    
    import java.util.List;
    
    public class StandardBean {
        private String name;
        private int id;
        private List<StandardBean> references;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public List<StandardBean> getReferences() {
            return references;
        }
    
        public void setReferences(List<StandardBean> references) {
            this.references = references;
        }
    
        @Override
        public String toString() {
            return "StandardBean{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    ", references=" + references +
                    "} " + super.toString();
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            StandardBean that = (StandardBean) o;
    
            if (id != that.id) return false;
            if (name != null ? !name.equals(that.name) : that.name != null) return false;
            if (references != null ? !references.equals(that.references) : that.references != null) return false;
    
            return true;
        }
    
        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + id;
            result = 31 * result + (references != null ? references.hashCode() : 0);
            return result;
        }
    }
    
    
    These three methods (equals, hashCode and toString) were generated by IDE. But, we have one drawback - their maintenance, i.e. we should carefully regenerate/update them. Also, readability is not very good. There are several alternative ways to solve this issue.

    1. Apache Commons Lang
    import org.apache.commons.lang3.builder.EqualsBuilder;
    import org.apache.commons.lang3.builder.HashCodeBuilder;
    import org.apache.commons.lang3.builder.ToStringBuilder;
    
    public class ApacheCommonsBean {
        
        ...
    
        @Override
        public String toString() {
            return new ToStringBuilder(this)
                    .append("name", name)
                    .append("id", id)
                    .append("references", references)
                    .toString();
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            ApacheCommonsBean that = (ApacheCommonsBean) o;
    
            return new EqualsBuilder()
                    .appendSuper(super.equals(o))
                    .append(name, that.name)
                    .append(id, that.id)
                    .append(references, that.references)
                    .isEquals();
        }
    
        @Override
        public int hashCode() {
            return new HashCodeBuilder(31, 17)
                    .appendSuper(super.hashCode())
                    .append(name)
                    .append(id)
                    .append(references)
                    .toHashCode();
        }
    
    }
    
    2. Google Guave
    import com.google.common.base.Objects;
    
    public class GuavaBean {
    
        ...
    
        @Override
        public String toString() {
            return Objects.toStringHelper(this)
                    .add("name", name)
                    .add("id", id)
                    .add("references", references)
                    .toString();
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null) return false;
    
            if (o instanceof GuavaBean) {
                final GuavaBean other = (GuavaBean) o;
                return Objects.equal(name, other.name)
                        && Objects.equal(id, other.id)
                        && Objects.equal(references, other.references);
            }
            return false;
        }
    
        @Override
        public int hashCode() {
            return Objects.hashCode(name, id, references);
        }
    
    }
    
    3. Java 7 Utils
    import java.util.Objects;
    
    public class JavaSevenBean {
       
        ...
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            JavaSevenBean that = (JavaSevenBean) o;
    
            return Objects.equals(name, that.name)
                    &&  Objects.equals(id, that.id)
                    && Objects.equals(references, that.references);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, id, references);
        }
    }
     
    Summary


    Both Apache Commons Lang and Google Guava are additional jars in you project. So, when should we use these libraries:
    • I prefer to avoid using additional libraries if I need only small amount of library functionality. I.e. don't add Apache Commons/Guava into your project if you need equals, hashCode and toString only.
    • Use full power of existent projects libraries. Don't reinvent the wheel. Almost all mid-size project has Apache Commons or Guava (or both).
    • Use modern Java 7 feature if it's possible in your current environment.
    • I can't make preference between Guava and Apache Commons. Just stick to the current project state and select library which will be more useful. Don't be dogmatic.
    IDE generated equals, hashCode and toString methods are not readable. And  in general cases they are boilerplate and hard to maintain. It's very easy to forget re-generate/update them.

    Links: