In the recent past, a team I was working with was facing an architectural decision regarding what technology and deployment footprint to go with for a greenfield project.
Its been about five months now since this application has been in production.
The use-case in question was to present a suite of REST services to front a large set of “master data” dimensions for a data warehouse as well securing that data (record level ACLs). In addition to this, the security ACLs it would manage needed to be transformed and pushed downstream to various legacy systems.
With that general use-case in mind, some other items of note that were to be considered:
- The specific use case of “REST services facade for master data” was generic in nature, so the security model was to be agnostic of the specific data set being secured and have the capability of being applied across different data sets for different clients.
- Changes for a given service should be easy to fix and deploy and independent of one another with minimal interruption.
- The services need to scale easily and be deployed across several different data centers which are a mix of traditional bare-metal/ESX vm’s as well as in the cloud (azure/aws). Tight coupling to each DC should be minimized when possible.
- The services stack would potentially serve as the hub for orchestrating various other ETL related processes for the warehouse, so adding new “services” should be easy to integrate into the larger application.
- Given the sensitivity of the data, all traffic should be secured w/ TLS and REST apis locked down w/ OAuth2 client credentials based access.
Given the above requirements and much discussion we decided to go with a container based microservices architecture.
First off, this team already had significant experience w/ the traditional monolithic approach to applications and had already run into the many shortcomings of this architecture over the long term. As new features needed to be deployed, it was becoming more of a pain to add new “services” to the monolith as it required the entire stack to be redeployed which is disruptive. Given this new application would have a similar lifecycle (new services needing to be added over time) we wanted to try a different approach…. and who was the new kid on the block? “microservices”; and it was time to get one’s feet wet.
This shop was primarily focused on NodeJS, LAMP and Java stacks so after doing some research the decision was made to go with Spring Cloud as the base framework to build this new suite of services. If one does any reading on the topic of microservices, you will quickly see such architectures involve many moving parts: service discovery, configuration, calling tracing (i.e. think google dapper), load balancing etc.
Do you want to write these pattern implementations this all yourself? Probably not; I sure didn’t. So after evaluating the space at the time, Spring Cloud was the most robust solution for this and one of its biggest selling points is that it was based on many of the great frameworks that have come out of Netflix’s OSS project (Eureka, Hystrix and more..)
Lastly the decision to go w/ Docker was really a no brainer. The services would potentially need to be deployed and moved across various data centers. By using Docker DevOps would be able to have a footprint and deployment process that would be consistent regardless of what data center we would be pushing to. The only data center specific particulars our DevOps guys had to care about was, setting up the Docker infrastructure (i.e. think Docker hosts on VMs via Ansible coupling to DC specific host provisioning APIs) and the DC specific load balancers, who’s coupling to the application was just a few IP’s and ports (i.e. the IPs of the swarm nodes with exposed ports of our Zuul containers). Everything downstream from that was handled by Docker Swarm and the microservices framework itself (discovery, routing etc)
The acronym for this services backend ended up being CELL which stands for… well whatever you want it to stand for…. I guess think of it (the app) as an organism made up of various cells (services). CELL’s services are consumed by various applications that present nice user interfaces to end users.
The above diagram gives a high level breakdown of its footprint. Its broken up into several services:
Core services that all other app services utilize:
- cell-discovery: Netflix Eureka: Participating services both register on startup and use this to discover the cell-config service (to bootstrap themselves) plus discover any other peer level services they need to talk to.
- cell-config: spring-cloud-config: Git sourced application configuration (w/ encryption support). Each application connects to this on startup to configure itself.
- oauth2-provider: All services are configured w/ an OAuth2 client credentials compliant token generation endpoint to authenticate and get tokens that all peer services validate (acting as resource servers)
- tracing-service: zipkin: All services are instrumented w/ hooks that decorate all outbound http requests (and interpret them upon reception) with zipkin compliant tracing headers to collect call tracing metrics etc. Background threads send this data periodically to the tracing service.
- cell-event-bus: kafka and spring-cloud-stream: Certain services publish events that other services subscribe to to maintain local caches or react to logic events. This provides a bit looser coupling than direct service to service communication; leveraging Kafka gives us the ability to take advantage of such concepts of consumer groups for different processing requirements. (i.e. all or one)
- cell-router: Netflix zuul: Router instances provide a single point of access to all application services under a https://router/service-name/ facade (discovered via the discovery service). Upstream data center specific FQDN bound load balancers only need to know about the published ports for the Zuul routers on the swarm cluster to be able to access any application service that is available in CELL.
- cell-service-1-N: These represent domain specific application services that contain the actual business logic implementation invoked via external callers. Over time, more of these will be added to CELL and this is where the real modularity comes into play. We try to stick to the principle of one specific service per specific business logic use-case.
As noted above, one of the requirements for CELL was that participating services could have data they manage, gated by a generic security ACL system. To fulfill this requirement, one of those domain specific apps is the cell-security service.
The cell-security service leverages a common library that both cell-security servers and clients can leverage to fulfill both ends of the contract. The contract being defined via some general modeling (below) and standard server/client REST contracts that can easily be exposed in any new “service” via including the library and adding some spring @[secConfig] annotations in an app’s configuration classes.
- Securable: a securable is something that can have access to it gated by a SecurityService. Securables can be part of a chain to implement inheritance or any strategy one needs.
- Accessor: is something that can potentially access a securable
- ACL: Binds an Accessor to a Securable with a set of Permissions for a given context and optional expression to evaluate against the Securable
- SecurableLocator: given a securable‘s guid, can retrieve a Securable or a chain of Securables
- AccessorLocator: given a accessor‘s guid, can retrieve the Accessor
- AccessorLocatorRegistry: manages information about available AccessorLocators
- SecurableLocatorRegistry: manages information about available SecurableLocators
- ACLService: provides access to manage ACLs
- PrincipalService: provides access to manage Principals
- LocatorMetadataService: provides access to manage meta-data about Securable|Accessor Locators
- ACLExpressionEvaluator: evaluates ACL expressions against a Securable
- SecurityService: Checks access to a Securable for a requesting Accessor
The model above is expressed via standard REST contracts and interfaces in code, that are to be fulfilled by a combination of default implementations and those customized by individual application CELL services who wish to leverage the security framework. There are also a few re-usable cell-security persistence libraries we created to let services that leverage this to their persist security data (both authoritative and local consumer caches) across various databases (Mongo DB and or JPA etc). As well a another library to hook into streams of security events that flow through CELL’s Kakfa event bus.
Spring Cloud impressions
When I started using Spring Cloud (in the early days of the Brixton release), I developed a love – hate relationship with it. After a few initial early successes with a few simple prototypes I was extremely impressed with the discovery, configuration and abstract “service name” based way of access peer services (via feign clients bound to the discovery services)…. you could quickly see the advantageous to using these libraries to really build a true platform that could scale to N in several different ways and take care of a lot of the boilerplate “microservices” stuff for you.
That said, once we really got into the developing CELL we ended up having two development paths.
The first being one team working on creating a set of re-usable libraries for CELL applications to leverage and integrate into the CELL microservice ecosystem. This consisted of creating several abstractions that would bring together some of the required spring cloud libraries, pre-integrated via base configuration for CELL, and just make it easier to “drop-in” to a new CELL app without having to wade into the details of spring cloud too much and just let the service developer focus on their service. The amount of time on this part was about 70% of the development effort, heavily front loaded in the start of the project.
The second being the other team using the latter to actually build the business logic services, which was the whole point of this thing in the first place. This accounted for about 30% of the work in the beginning and today… about 80-90% of the work now that the base framework of CELL is established.
The hate part (well not true hate, but you know what I mean… friendly frustration) of this ended up being the amount of man hours spent in the start of the project dealing/learning spring-cloud. There is a tangible learning curve to be aware of. Working around bugs, finding issues in spring-cloud, both real ones or just working through perceived ones via misunderstandings due to the complexity of spring-cloud itself.
I’m not going to go into each specific issue here, however there were simply a lot of issues and time spent debugging spring cloud code trying to figure out why certain things failed or to learn how they behaved so we could customize and properly configure things. In the end most of the issues could be worked around or were not that hard to fix…. its just the time it took to figure out the underlying causation’s, produce a reproducible sample and then convey it to the spring-cloud developers to get help with. (The spring-cloud developers BTW are excellent and VERY responsive) kudos to them for that.
Lastly, taking each CELL artifact (jar) and getting it wrapped up in a Docker container was not an huge ordeal. In the deployed footprint, each CELL artifact is a separate Docker Swarm Service that is deployed on its own overlay network (separate one per CELL version). As stated previously, the CELL router (Zuul) is the only service necessary to be exposed on a published swarm port and then upstream datacenter load balancers can just point to that.
So would I recommend Spring-Cloud?
Yes. Spring Cloud at its heart is really an pretty impressive wrapper framework around a lot of other tools that are out there for microservices. It has a responsive and helpful community. (definitely leverage Gitter.im if you need help!) The project has matured considerably since I first used it and many of the issues I was dealing with are now fixed. Compared to writing all the necessary things to have a robust microservices ecosystem yourself….. I’ll take this framework any day.
Final note. I would NOT recommend using spring-data-rest. We used that on a few of the CELL application logic services and its main benefit of providing you a lot of CRUD REST services in a HATE-OS fashion…. its just not that easy to customize the behavior of, has a lot of bugs and just generally was a pain to work with. At the end of the day it would have just been easier to code our own suite of CRUD services instead of relying on it.
To sum it up; Dropwizard rocks.
I’ve done quite a bit of WS development both the on client side and server side; interacting with both SOAP, REST and variants of XML/JSON RPC hybrid services. For my latest project I need to expose a set of REST services to a myriad of clients: phones, fat JS clients etc. This application also needs to talk to other nodes or “agents” that are also doing work in a distributed cloud environment. The core engine of this application really has no need for a bloated higher level MVC/gui supporting stack and bringing that into this library would just be a pain. I’ve always like the simplicity of being able to skip the whole JEE/Spring/Tomcat based container stack and just do a plain old “java -jar”… run my application…. but the reality of being able to do that has been lacking… until now.
In looking at the available options for picking the framework to use (such as Restlet, Spring-MVC REST, Spring Data REST etc and others), I immediately became discouraged when looking at examples for setting them up; they are full of complexity and lots of configuration and generally require a full application server container to run within, which just adds further complexity to your setup.
Then I stumbled across Dropwizard by the folks at Yammer. I encourage everyone reading this to just try the simple Hello World example they have on their site. If you have any experience in this space and an appreciation and vision for decoupling; you will immediately recognize the beauty of this little framework and the power it can bring to the table from a deployment standpoint. Build your core app engine back-end library as you normally would, toss in Dropwizard, expose some REST services to extend your interfaces to the outside world; throw it up on a server and “java -jar myapp server myconfig.yml” and you are ready to rock. (they make this possible by in-lining Jetty). Create a few little JS/HTML files for a fat JS client, (i’d recommend Angular) and hook into your REST services and you will have an awesome little decoupled application.
Today I pushed up some source to Github for a utility I was previously working on to load data from USPS AIS data files into HBase/Mysql using Hadoop mapreduce and simpler data loaders. Source @ https://github.com/bitsofinfo/usps-ais-data-loader
This project was originally started to create a framework for loading data files from the USPS AIS suite of data products (zipPlus4, cityState). The project has not been worked on in a while but I figured I’d open-source it and maybe some folks would like to team up to work on it further, if so let me know! Throwing it out there under the Apache 2.0 license. Some of the libs need updating etc as well, for instance it was originally developed w/ Spring 2.5.
USPS AIS data files are fixed length format records. This framework was created to handle bulk loading/updating this data into a structured/semi-structured data store of address data (i.e. MySql or HBase). It is wired together using Spring and built w/ Maven. A key package is the “org.bitsofinfo.util.address.usps.ais” package which defines the pojos for the records, and leverages a custom annotation which binds record properties to locations within the fixed length records which contain the data being loaded.
Initial loader implementations include both a single JVM multi-threaded version as well as a second one that leverages Hadoop Mapreduce to split the AIS files up across HDFS and process them in parallel using Hadoop mapreduce nodes to ingest the data much faster then just on one box. Both of these obviously operate asynchronously given a load job submission. Ingestion times are significantly faster using Hadoop.
This project also had a need for a Hadoop InputFormat/RecordReader that could read from fixed length data files (none existed), so I created it for this project (FixedLengthInputFormat). This was also contributed as a patch to the Hadoop project. This source is included in here and updated for Hadoop 0.23.1 (not yet tested), however the patch that was submitted to the Hadoop project is still pending and was compiled under 0.20.x. The 0.20.x version in the patch files was tested and functionally running on a 4 node Hadoop and Hbase cluster.
You can read more about the fixed length record reader patch @
The USPS AIS products have some sample data-sets available online at the USPS website, however for the full product of data-files you need to pay for the data and/or subscription for delta updates. Some of the unit-tests reference files from the real data-sets, they have been omitted, you will have to replace them with the real ones. Other unit tests reference the sample files freely available via USPS or other providers.
Links where USPS data files can be purchased:
For those of you out there who would like to get Restlet 2.0 (currently the M5) release up integrated with your existing Spring application, hopefully this post will be of some help. I recently had to do this and unfortunately the documentation related to Spring integration on the Restlet site is scattered across various docs and some of it appears out of date. What I am describing below worked with source code straight from the Restlet SVN trunk just before the M5 release so you should be good to go if you use the M5 release (JEE edition)
First off, I am assuming you have an existing web application with a web.xml file and are using Spring. Secondly I am just trying to give you some working web.xml and the corresponding Spring configuration to get up and running. I am not explaining the details of how Restlet works as you can find that on the Restlet site.
First you will want to make sure you have the Restlet JEE 2.0 M5 edition. Make sure you grab the JEE version and not the JSE version as the latter does not include the Spring integration extension. Once downloaded extract the ZIP to a location on your drive. The JEE zip package contains a ton of Restlet Jar files. The three we care about are
org.restlet.jar, org.restlet.ext.spring.jar, org.restlet.ext.servlet.jar
If you are using Maven you can add the following repository and dependencies to your POM by using the repository instructions on the Restlet site. NOTE as of today: the Restlet repository currently does NOT have the M5 release up there so you are going to manually have to add the M5 jar to your repository by doing the following for each of the 3 jars.
mvn install:install-file -Dfile=/PATH/TO/RESTLET-m5-ZIP-EXTRACT-DIR/lib/org.restlet.jar -DgroupId=org.restlet -DartifactId=org.restlet -Dversion=2.0-SNAPSHOT-M5 -Dpackaging=jar
mvn install:install-file -Dfile=/PATH/TO/RESTLET-m5-ZIP-EXTRACT-DIR/lib/org.restlet.ext.spring.jar -DgroupId=org.restlet -DartifactId=org.restlet.ext.spring -Dversion=2.0-SNAPSHOT-M5 -Dpackaging=jar
mvn install:install-file -Dfile=/PATH/TO/RESTLET-m5-ZIP-EXTRACT-DIR/lib/org.restlet.ext.servlet.jar -DgroupId=org.restlet -DartifactId=org.restlet.ext.servlet -Dversion=2.0-SNAPSHOT-M5 -Dpackaging=jar
The above 3 commands will manually install the three Jars into your Maven repository. Next you can configure your POM to add the official Maven repository plus the dependencies to the 3 Jars you installed above. Note that the repository entry is sort of meaningless at this point because you manually installed the jars above. It is IMPORTANT that the
elements in your dependencies below MATCH exactly the versions you specified in the commands above!
<repository> <id>maven-restlet</id> <name>Public online Restlet repository</name> <url>http://maven.restlet.org</url> </repository> <dependency> <groupId>org.restlet</groupId> <artifactId>org.restlet</artifactId> <version>2.0-SNAPSHOT-M5</version> </dependency> <dependency> <groupId>org.restlet</groupId> <artifactId>org.restlet.ext.spring</artifactId> <version>2.0-SNAPSHOT-M5</version> </dependency> <dependency> <groupId>org.restlet</groupId> <artifactId>org.restlet.ext.servlet</artifactId> <version>2.0-SNAPSHOT-M5</version> </dependency>
Ok, great. Next we need to configure your
web.xml, open it up and add the following entries in the appropriate spots:
<servlet> <servlet-name>myRESTApi</servlet-name> <servlet-class>org.restlet.ext.spring.SpringServerServlet</servlet-class> <init-param> <param-name>org.restlet.component</param-name> <!-- this value must match the bean id of the Restlet component you will configure in Spring (below) --> <param-value>restletComponent</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>myRESTApi</servlet-name> <url-pattern>/my/REST/api/*</url-pattern> </servlet-mapping>
Now your web.xml is configured to take all requests to
/my/REST/api/* and send those to a Restlet Component which you will wire up in your Spring configuration. So... bring up your applicationContext.xml or whatever you have it named and add the following entries:
<!-- our SpringComponent which binds us to the Restlet servlet configured above --> <bean id="restletComponent" class="org.restlet.ext.spring.SpringComponent"> <!-- the defaultTarget for this component is our Restlet Application --> <property name="defaultTarget" ref="myRestletApplication" /> </bean> <!-- your Restlet application. This class extends "org.restlet.Application" --> <bean id="myRestletApplication" class="my.restlet.MyRestletApplication"> <!-- all requests to this Application will be sent to myPath2BeanRouter --> <property name="root" ref="myPath2BeanRouter"/> </bean> <!-- This router automagically routes requests to beans that extend org.restlet.resource.ServerResource or org.restlet.Restlet who's name starts with a "/" slash which matches the request--> <bean name="myPath2BeanRouter" class="org.restlet.ext.spring.SpringBeanRouter"/> <!-- This extension of org.restlet.resource.ServerResource bean will handle all requests to made to /my/REST/api/myResource (GET/POST/PUT etc) This class extends "org.restlet.Restlet" or "org.restlet.resource.ServerResource" --> <bean name="/myResource" autowire="byName" scope="prototype" class="my.restlet.package.resources.MyResourceBean"> <property name="somePropertyOfMine" ref="someOtherSpringBean"/> </bean>
Ok, well if you were having trouble trying to get Spring working with Restlet I hope this helped get you rolling. Restlet is a cool project that works great and can get a REST API up and running pretty quickly (granted you are good at crawling through somewhat scattered documentation) Here are a few other links which you may want to reference:
Also, I am posting the following error messages that troubled me when trying to get this to work. The configuration I show above was the result of getting beyond the below errors by using the correct fixed releases. To AVOID the errors below, ENSURE you are using Restlet 2.0 M5 or a custom build from the trunk. Prior to 9/25/09 people were getting the errors below.
No target class was defined for this finder
Complete Message org.restlet.ext.spring.SpringFinder $$ EnhancerByCGLIB
This post will be short and sweet, but for those of you using Spring Security and come across this exception, hopefully this post will be of some help to you. Here is the exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_filterChainList': Cannot resolve reference to bean '_filterSecurityInterceptor' while setting bean property 'filters' with key ; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_filterSecurityInterceptor': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: AccessDecisionManager does not support secure object class: class org.springframework.security.intercept.web.FilterInvocation at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:275) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:104) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:287) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:126)
I received this exception today when I began to add some http/web level security into my application which already had some pre-existing custom configuration for my own AuthenticationManager, AccessDecisionManager (w/ custom voters) and AuthenticationProvider. When I added the following configuration to my Spring configuration the above exception was thrown on the next Maven test run:
... custom auth provider, auth manager, voters above.... <!-- my custom accessDecisionManager config --> <bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased"> <property name="decisionVoters"> <list> <ref bean="myCustomAccessVoter"/> </list> </property> </bean> <!-- the NEW entry I added which triggered the exception --> <security:http access-decision-manager-ref="accessDecisionManager"> <security:intercept-url pattern="/**" access="ROLE_USER" /> <security:form-login/> </security:http>
The reason for the above exception is because my AffirmativeBased AccessDecisionManager did not have any decisionVoters who responded with “true” when passed a “FilterInvocation” object to their
supports(Class clazz) method.
If you have a custom AccessDecisionVoter like I did above, you need to begin returning
true in calls to support(Class clazz) when passed instances of “FilterInvocation” objects, while still only returning true for only the ConfigAttributes you care about when
support(ConfigAttribute attr) is called on your voter. Secondly you should add an
RoleVoter to your list of
decisionVoters for your AccessDecisionManager configuration.
There is more to it than just the quick fix listed above, however that is for you to implement in your application. I just wanted to post this to give people a pointer in the right direction as to what is causing this exception. Hope it helped!
Ok so today I was working on some JUnit tests within Spring using AbstractTransactionalJUnit4SpringContextTests (yes, wow what a long class name). For those of you unfamiliar with this class, basically if you extend your unit test class from this class, every
@Test annotated test method will run within a transaction, with the default behavior being that a rollback is called after each
@Test method’s execution (unless you declare so otherwise). Pretty convenient.
My simple test case was leveraging a DAO that derived from
HibernateDaoSupport and the test inserted a few records. When the test was complete, another method annotated with
@AfterTransaction would verify that the data did NOT exist in the table, which was expected due to the rollback that occurs after each test case…… well my asserts were failing because Hibernate created the MySQL tables using MyISAM, which does not support transactions. If you encounter this kind of issue, all you have to do is change your Hibernate dialect to use the
MySQL5InnoDBDialect (for InnoDB storage which does transactions) within your session factory configuration as such:
... <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> ...
This is a review of the book “Spring Recipes” by Gary Mak
I picked up this book as a supplement to the “Spring in Action” 1st/2nd editions that I have. This book takes the same approach as the O’Reilly Cookbook series and covers Spring 2.5. Basically it is a task/problem oriented book. Each little section addresses a small specific task such as “Unit testing Spring MVC Controllers” or ” Checking Properties with Dependency Checking”. Despite the task oriented recipes approach, the book is still structured in a progressive manner whereby a newcomer to the framework could start reading from the start to the end, learning the basics to the more complex. Each recipe is structured with a “Problem/Solution/How it works” section which presents a consistent approach to all content within the book. The examples are clear and in many places the author provides helpful diagrams to help the reader visualize the relationships involved in a particular problem.
I’ve gone through about 1/2 the book and stopped until I had an actual need for a specific recipe, however I do recommend the book and it is something I have cracked open as I’ve encountered problems.
Skills: Java / Beginner’s all the way to Advanced
This is a review for “Spring in Action” 2nd Edition by Craig Walls
Spring in Action covers 2.0. The book is excellent and walks the reader through the basics of IoC, bean injection, AOP and then on to virtually every major component of this framework. For the database side of things, they cover basic JDBC usage, Hibernate, iBATIS, and JPA etc. Beyond that, Spring-WS, JMS, EJB 2.x and 3, JNDI and JMX integration is given excellent coverage. Then on to the client side it covers Spring MVC, Webflow, how to integrate w/ Struts, WebWork, JSF and even some DWR.
The book is huge covering over 700 pages and mine is currently tagged with about 50 page flags marking off all the topics I need quick access to. I read the book mainly to get up to speed on components I was unfamiliar with and would recommend this to anyone who does anything with Spring. The content in here is excellent and walks you through the entire framework in a logical approach, starting with the simpler stuff up to the advanced.
Skills: Java, Intermediate to Advanced. Excellent desk side reference for the heavy user.