Monthly Archives: August 2013

Tomcat, JNDI and Spring bean application configuration

While setting up a Continuous Integration environment recently i faced an issue related to application (REST API in this case) configuration not being deployment-environment independent. Namely as the code pushed to Git repository and picked up by Jenkins build server was later on automatically deployed across several server environments (DEV, INT, STAGING, PROD) it turned out that in each of those environments the API application (war archive deployed in Tomcat container) requires to be fed with a specific/different configuration (environment-specific settings).

 

This is how i managed to solve this issue:

 

1. I created the following Tomcat context entry in “conf/context.xml” file:

<Context>
    <Resource name="config/Api"
        auth="Container"
        type="com.mycompany.model.ApiConfigBean"
        factory="com.mycompany.api.jndi.CustomApiJNDIFactory"
        scheme="https"
        server="api.mycompany.com"
        port="8443"
        version="1.0"
        sso="sso.mycompany.com" />
</Context>

 

2. Created the “CustomApiJNDIFactory” class:

public class CustomApiJNDIFactory implements ObjectFactory {

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?,?> environment) throws Exception {

        validateProperty(obj, "Invalid JNDI object reference");

        String scheme = null;
        String host = null;
        String port = null;
        String version = null;
        String sso = null;

        Reference ref = (Reference) obj;

        Enumeration props = ref.getAll();

        while (props.hasMoreElements()) {

            RefAddr addr = props.nextElement();
            String propName = addr.getType();
            String propValue = (String) addr.getContent();

            switch (propName) {
                case "scheme":
                    scheme = propValue;
                    break;
                case "host":
                    host = propValue;
                    break;
                case "port":
                    port = propValue;
                    break;
                case "version":
                    version = propValue;
                    break;
                case "sso":
                    sso = propValue;
                    break;
            }

        }

        // validate properties
        validateProperty(scheme, "Invalid or empty scheme type");
        validateProperty(host, "Invalid or empty server host name");
        validateProperty(port, "Invalid or empty port number");
        validateProperty(version, "Invalid or empty API version number");
        validateProperty(sso, "Invalid or empty SSO server name");

        //create API Configuration Bean
        return new ApiConfigBean(scheme, host, port, version, sso);

    }

    /**
     * Validate internal String properties
     *
     * @param property the property
     * @param errorMessage the error message
     * @throws javax.naming.NamingException
     */
    private void validateProperty(String property, String errorMessage) throws NamingException {

        if (property == null || property.trim().equals("")) {
            throw new NamingException(errorMessage);
        }

    }

}

 

3. Defined an jndi-lookup entry in my “spring-api-context.xml” file that will read Tomcat JNDI configuration entry and expose it as a Spring bean of name jndiApi:

<jee:jndi-lookup id="jndiApi" jndi-name="java:/comp/env/config/Api" expected-type="com.mycompany.model.ApiConfigBean" />

 

4. Created the “jndiApi” Spring bean backing pojo

public class ApiConfigBean {

    private String scheme;
    private String host;
    private String port;
    private String version;
    private String sso;

    public ApiConfigBean(String scheme, String host, String port, String version, String sso) {
        this.scheme = scheme;
        this.host = host;
        this.port = port;
        this.version = version;
        this.sso = sso;
    }

    // getters and setters ommited.

}

 

5. and finally wired-in the bean to my classes where i needed to make use of the “externalized” configuration:

@Autowired
@Qualifier("jndiApi")
private ApiConfigBean apiConfigBean;

    public void foo() {

        String host = apiConfigBean.getHost();
        ...

    }

 

 

That’s it! Have a wonderful day! 🙂

Redis introduction

After reading another great book from Manning – Redis in Action, authored by Dr. Josiah L Carlson (whom you may know from the “Redis DB” mailing list on Google), i thought you may be interested in key takeaways.

 

Key facts:

  • NoSQL database (key/value store)
  • Stores a mapping of keys to five different types of values (strings, lists, sets, hashes, sorted sets)
  • Supports in-memory and and disk persistence
  • Supports master/slave replication to scale read performance
  • Support for client-side sharding to scale write performance
  • Publish/Subscribe capabilities
  • Support for scripting (equivalent of stored procedures)
  • Partial transaction support
  • Support of bulk operations

+ “Sharding is a method by which you partition your data into different pieces. In this case, you partition your data based on IDs embedded in the keys, based on the hash of keys, or some combination of the two. Through partitioning your data, you can store and fetch the data from multiple machines, which can allow a linear scaling in performance for certain problem domains.”

 

What happens when my server gets turned off?

  • Redis has two different forms of persistence available for writing in-memory data to disk in a compact format
    • point-in-time dump, either:
      • when certain conditions are met (a number of writes in a given period), or
      • when one of the two dump-to-disk commands is called.
    • append-only file that writes every command that alters data in Redis to disk as it happens. Depending on how careful you want to be with your data, append-only writing can be configured to:
      • never sync,
      • sync once per second, or
      • sync at the completion of every operation

 

Master/slave replication:

  • A good failover scenario if the server that Redis is running on crashes
  • Slaves connect to the master and receive an initial copy of the full database.
  • As writes are performed on the master, they’re sent to all connected slaves for updating the slave datasets in real time.
  • With continuously updated data on the slaves, clients can then connect to any slave for reads instead of making requests to the master.

 

Random writes:

  • are always fast, because data is always in memory,
  • queries to Redis don’t need to go through a typical query parser/optimizer

 

Five data structures available in Redis

Redis Data Types

 

Key takeaways:

  • Redis is:
    • fast – operates on in-memory data sets
    • remote – accessible to multiple clients/servers
    • persistent – opportunity to keep data on disk
    • scalable – via slaving and sharding