Easily integrate Spring into Dropwizard

Comment

Easily integrate Spring into Dropwizard

Dropwizard is a really nice, simple framework for developing RESTful web services. Out of the box it comes with all kinds of nice features by leveraging a collection of mature libraries in a straightforward manner. The main libraries it uses are Jetty for web serving, Jersey for REST, Jackson for JSON processing, along with others such as Google Guava, Hibernate Validator, Liquibase for migrations, and more. It also comes with out of the box support for JDBI, a simple "SQL convenience library for Java", as well as Hibernate.

The JDBI and Hibernate support provide the basics needed to get going with either of those frameworks, and the Hibernate support provides the @UnitOfWork annotation for declarative transaction demarcation which you can apply to Jersey resource methods. Dropwizard then takes care of automatically opening a Hibernate session, starting a transaction, and either committing or rolling back that transaction and finally closing the session when the resource method completes. For many use cases this will be all you need or want.

In certain situations, though, you might want more control over transactions and for this Spring is often a good choice. Specifically, you might want to take an existing Spring backend codebase and integrate it into a RESTful web service using Dropwizard. Or maybe you are using Hibernate in a Dropwizard application but you don't want the transaction scoped around and entire resource method (which is what using the @UnitOfWork annotation gives you out of the box). Maybe you aren't using Hibernate and are using JDBI, Spring JDBC, or Spring JPA and want to use Spring's native @Transactional support. Or maybe you are not even using a relational database, and instead are using one of the Spring Data projects that support transactions in Neo4J, MongoDB, Redis, etc.

For situations like those described above, what you want is a simple way to use Spring in your Dropwizard application to manage transactions in DAOs or service classes, but not much else. In other words you don't need or want to use all the other many features provided in Spring, but you do want to take advantage of its automatic connection and transaction management. For Dropwizard projects I've been working on recently, we came up with a very simple abstraction and simple pattern to integrate Spring using a builder-style class to create application contexts. This makes Dropwizard configuration and other objects like ManagedDataSources available to the Spring context.

For example, suppose you will be using Hibernate in a Dropwizard application but want to use Spring to manage transactions. Suppose also that you configure a DataSourceFactory via the normal Dropwizard configuration mechanism and want that to be used by Spring when creating the Hibernate session factory. Also suppose you want the Dropwizard configuration object to be available to the Spring context. Assuming you have a simple "todo" Dropwizard application with a TodoApplication class and a TodoConfiguration class, you can write code like the following in the TodoApplication class:


@Override
public void run(TodoConfiguration configuration, Environment environment) throws Exception {
    DataSourceFactory dataSourceFactory = configuration.getDataSourceFactory();
    ManagedDataSource dataSource = dataSourceFactory.build(environment.metrics(), "dataSource");
 
    ApplicationContext context = new SpringContextBuilder()
            .addParentContextBean("dataSource", dataSource)
            .addParentContextBean("configuration", configuration)
            .addAnnotationConfiguration(TodoSpringConfiguration.class)
            .build();

    TodoDao todoDao = context.getBean(TodoDao.class);
    TodoResource todoResource = new TodoResource(todoDao);
    environment.jersey().register(todoResource);
}

In the above code, SpringContextBuilder is a very simple builder-style class that lets you create Spring application contexts by specifying parent beans that should be available to other beans (e.g. the "dataSource" and "configuration" beans), and then adding either annotation-based configuration classes or XML configuration file locations. This class is available on GitHub here.

The above code creates a Spring context from which you can then extract the beans, such as TodoDao, that will be used by Jersey resource classes. Note that we're not using Spring for autowiring dependencies in the TodoResource class, and are simply passing the DAO to its constructor. The resource class has no idea that the DAO is actually a Spring-managed bean, nor does it need to. This also makes it very easy to inject a mock into the resource class for unit tests.

So the only thing left to do is actually create the Spring application context. In the above code, we're using the TodoSpringConfiguration class which is a Java Config-based configuration class. The code below shows the basics, with a few details omitted:


@Configuration
@EnableTransactionManagement
@ComponentScan(basePackageClasses = TodoDao.class)
public class TodoSpringConfiguration {

    @Autowired
    private DataSource _dataSource;

    @Autowired
    private TodoConfiguration _configuration;

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(_dataSource);
        sessionFactory.setPackagesToScan(Todo.class.getPackage().getName()));
        sessionFactory.setNamingStrategy(ImprovedNamingStrategy.INSTANCE);
        sessionFactory.setHibernateProperties(hibernateProperties());
        return sessionFactory;
    }

    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
        return new HibernateTransactionManager(sessionFactory);
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    private Properties hibernateProperties() {
        // details elided...
    }

}

As you can see, the TodoSpringConfiguration class is just a plain Spring Java-based configuration class. The only real noteworthy thing is how we used @Autowired to make the data source and Dropwizard configuration objects available. The sessionFactory() configuration method then simply uses the data source when constructing the session factory. Other beans could use the Dropwizard configuration object, for example to extract other configuration such as Hibernate-specific configuration properties.

That's really all there is to it. You just use SpringContextBuilder to create your Spring context, extract the beans you need, and pass them to Jersey resource classes or any other classes such as health checks. The classes using the Spring-managed beans can simply use them without needing to be aware of their Spring-managed nature. All of which helps keep your Dropwizard code clean while still gaining the advantage of powerful Spring features. The "todo" example application code is available on GitHub here along with instructions for building and running it.

Comment

Extjs Dynamic Stateful Grid

Comment

Extjs Dynamic Stateful Grid

Do you have a javascript MVC framework for your existing web application?  Tired of spending development cycles updating your application views every time your data provider sends something new?  Yeah, me too.  Thankfully there are a good number javascript MVC frameworks that can be used to display dynamic data AND save the display state of said data.

Recently I wanted to build such a web application that would display dynamic data key/value pairs as well as saving the user's view state of said data.  I’ve used the Extjs framework to build various web applications and I’ve been impressed with the Ext.grid.Panel’s features as well as the ease of creating ‘stateful’ components with the Extjs framework. While there isn’t anything directly out of the box that will allow you to create a dynamic stateful grid panel, the Extjs framework does give you the tools to build your own stateful grid component without modifying any of their source code.

My requirements consisted of:

  • Dynamically display json-based messages in a table (without knowing the json nested structure or key/value pairs of the data).
  • Allow for user to customize the table (sorting, grouping, columns displayed, column order, column width).
  • Save state of the user-customized table and reload state upon user return to web application.

Blog post assumptions:

  • You are somewhat familiar with Extjs.
  • You know how to setup an Extjs State Provider.
  • You are using a ‘standard’ Extjs store (I’m using an ajax proxy with my Extjs store).

Server Side Steps

Step 1: Flatten your result set.  The Dynamic Stateful Grid described in this post worked well with a flattened array of json objects.

Step 2: Define the 'dynamic columns' in your response to the client.

An Extjs grid will allow the grid columns to be defined dynamically.  To do this, the response to the Grid’s store must include a ‘metaData’ element which defines the fields and columns based on the data being served.  The ‘metaData’ element looks like this example:

{
  metaData: {
    columns: [
      {‘header’: key1, ‘dataIndex’: key1, ‘stateId’: key1},
      {‘header’: key2, ‘dataIndex’: key2, ‘stateId’: key2}
    ],
    fields: [
      {‘name’: key1, ‘type’: ‘string’},
      {‘name’: key2, ‘type’: ‘string’}
    ]
  },
  items: [
    {‘field1’: value1,  ‘field2’: value2, ‘field3’: value3},
    {‘field4’: value4,  ‘field5’: value5, ‘field6’: value6}
  ]
}

Client Side Steps

Step 1: Define a Dynamic Stateful Grid Panel (code below works with Extjs 4, 5, and 6)

When the Extjs grid sees the ‘metaData’ element in it’s store results, the ‘metachange’ event is fired.  We will need to override both the ‘metachange’ and ‘beforestatestave’ events to keep track of the columns shown/hidden, the column order, and the column widths.

Ext.define('Ext.grid.DynamicStatefulGridPanel', {
extend: 'Ext.grid.Panel',
alias: 'widget.dynamicstatefulgrid',
stateful: true,

initComponent: function() {
  var _this = this;
  this.on({
    beforestatesave: function(cmp, state, eOpts) {
      var savedState = Ext.state.Manager.getProvider().state[_this.stateId];

      // don't save a state of 0 columns shown
      if (state && state.columns.length === 0 && savedState && savedState.columns.length === 0) {
        return false;
      }

      // if the current state has 0 columns shown, replace it with the old state and 
      // return false to prevent saving an empty state
      if (state.columns.length === 0 && savedState && savedState.columns.length > 0) {
        state = savedState;
        return false;
      }

      // if a metachange event was fired previously, reset the metachangefired flag 
      // to prevent saving the incorrect state
      if (_this.store.metachangefired) {
        _this.store.metachangefired = false;
        return false;
      }

      // if new state and old state are present, update the column hidden and width attributes
      if (state && state.columns.length > 0 && savedState && savedState.columns.length > 0) {
        $.each(savedState.columns, function(index, savedColumn) {
          $.each.(state.columns, function(stateIndex, newColumn) {
            if (savedColumn['id'] === newColumn['id']) {
              if (savedColumn['hidden'] && newColumn['hidden'] != false) {
                newColumn['hidden'] = savedColumn['hidden'];
              }
              if (!newColumn['width'] && savedColumn['width']) {
                newColumn['width'] = savedColumn['width'];
              }
            }
          });
        });
      }
      _this.metachangefired = false;
    },
    beforereconfigure: function(cmp, store, columns, oldStore, oldColumns, eOpts) {
      _this.store.metachangefired = false;
    }
  });
  this.callParent(arguments);
},

bindStore: function(store) {
  var _this = this;
  //initialize the metachangefired flag on the store
  store.metachangefired = false;
  this.callParent(arguments);
  store.mon(store, 'metachange', function(store, meta) {
      store.metachangefired = true;

      // get the columns passed in on the metachange event. 
      // Note: these columns don't have the savedState applied yet
      var metaDataColumns = meta.columns;

      // initialze array to track saved state column order
      var reorderedMetaColumns = [];

      var provider = Ext.state.Manager.getProvider();
      var state = provider.state;

      // if a state is present for this grid (_this.stateId), update the columns 
      // with the saved state from the state provider
      if (state[_this.stateId]) {
        $.each(metaDataColumns, function(index, metaDataColumn) {
          $.each(state[_this.stateId]['columns'], function(stateIndex, stateColumn) {
            if (metaDataColumn['dataIndex'] === stateColumn['id']) {
              if (stateColumn['hidden']) {
                metaDataColumn['hidden'] = stateColumn['hidden'];
              }
              if (stateColumn['width']) {
                metaDataColumn['width'] = stateColumn['width'];

              }
            }
          });
        });
      }

      if (reorderedMetaColumns.length === 0) {
        reorderedMetaColumns = metaDataColumns;
      }

      // reconfigure the grid with the saved state applied to 
      // the dynamic metaData columns
      _this.reconfigure(store, reorderedMetaColumns);
    }
  }
});

Step 2: Instantiate your Stateful Grid Panel

var _this = this;
_this.store = Ext.create(‘My.ext.RecordStore’, {});
_this.grid = Ext.create(‘Ext.grid.DynamicStatefulGridPanel’, {
	store: _this.store,
	features: [{ftype: ‘grouping’}];
	columns: [],
	stateId: ‘my-dynamic-stateful-grid’
});

Step 3: Profit!

That's it!  Once your Extjs data Store is hooked up to a data source and your results are transformed into a flattened list structure, you'll be able to display whatever key/value pairs are present in the results set as well as save the user's state of columns displayed, column widths, column order, sorting and grouping.

 

Comment