Adaptive Spring Applications with Profiles and More

4,891 views

Published on

Speakers: Josh Long and Kevin Nilson
It’d be nice to assume everything remains the same from one environment to another, but the realities of today’s deployment targets (clouds, app servers, etc.) make this difficult. An application may target one in-memory database in development and target a traditional database in production. A/B testing is a common practice that lets you incrementally expose potentially high risk features. Feature switches can be invaluable; should something go wrong, you can revert to a known state. All of these use cases, and more, can be handled using the Spring framework.
Join JavaOne Rock Star and Java Champion Kevin Nilson and Spring Developer Advocate Josh Long for a look at how you can run your application in differing environments using the Spring Framework.

Published in: Technology, Education

Adaptive Spring Applications with Profiles and More

  1. 1. Adaptive Spring Applications with Profiles and More by Josh Long and Kevin Nilson © 2013 SpringOne 2GX. All rights reserved. Do not distribute without permission. Friday, September 20, 13
  2. 2. About Josh Long Developer Advocate interested in big data, the open web, cloud and all things Spring 2 Friday, September 20, 13 @starbuxman josh.long@springsource.com
  3. 3. KEVIN NILSON About  Kevin  Nilson • • • • • • • • Chromecast  Developer  Advocate  -­‐  Google Java  Champion 3  Time  JavaOne  Rock  Star Co-­‐Author  of  Web  2.0  Fundamentals Leader  Silicon  Valley  Java  User  Group Leader  Silicon  Valley  JavaScript  Meetup Leader  Silicon  Valley  Google  Developer  Group Taught  7  Course  @  College  of  San  Mateo,  CIS Friday, September 20, 13
  4. 4. WELCOME Welcome Friday, September 20, 13
  5. 5. ADAPTING THE SYSTEM AT CONFIGURATION TIME Adapting Beans at Construction Time Friday, September 20, 13
  6. 6. TAILORING BEAN CONSTRUCTION manages the life cycle of beans in the ApplicationContext package some; public class Bean { public void init(){ } public void destroy(){ } } <bean init-method="init" class="some.Bean" destroy-method="destroy"> ... </bean> Friday, September 20, 13
  7. 7. TAILORING BEAN CONSTRUCTION manages the life cycle of beans in the ApplicationContext package some; public class Bean { @PostConstruct public void init(){ } @PreDestroy public void destroy(){ } } Friday, September 20, 13
  8. 8. TAILORING BEAN CONSTRUCTION manages the life cycle of beans in the ApplicationContext package some; public class Bean implements InitializingBean, DisposableBean { public void afterPropertiesSet(){ } public void dispose(){ } } Friday, September 20, 13
  9. 9. TAILORING BEAN CONSTRUCTION manages the life cycle of beans in the ApplicationContext Spring uses the no-arg constructor of your bean to create it. Friday, September 20, 13
  10. 10. TAILORING BEAN CONSTRUCTION manages the life cycle of beans in the ApplicationContext Historically: we’ve used FactoryBeans to mix things up public class CronTriggerFactoryBean implements FactoryBean<CronTrigger> { ... public CronTrigger getObject(){ ... } } Friday, September 20, 13
  11. 11. TAILORING BEAN CONSTRUCTION but with Java configuration, you don’t need the indirection anymore.. @PropertySource("/config.properties") @Configuration public class ServiceConfiguration { @Bean public DataSource postgres(Environment e) { // ... } @Bean public CustomerService customerService(DataSource ds) { return new CustomerService(ds); } } Friday, September 20, 13
  12. 12. TAILORING BEAN CONSTRUCTION If you think about it... factory methods are everywhere! (They’re even a pattern!) javax.inject.Provider<DataSource> dataSourceProvider o.sf.beans.FactoryBean<DataSource> dataSourceFactoryBean and what about.. java.util.concurrent.Callable<DataSource> dataSourceCallable Friday, September 20, 13 ... ?
  13. 13. TAILORING BEAN CONSTRUCTION factory methods are going to be awesome with Java 8 Provider<DataSource> dataSourceProvider = ()->{ // ... }; Friday, September 20, 13
  14. 14. ADAPTING THE SYSTEM AT CONFIGURATION TIME The Environment and Profiles Friday, September 20, 13
  15. 15. ASKING QUESTIONS OF THE ENVIRONMENT... the environment in which an application runs isn’t always certain... Friday, September 20, 13
  16. 16. ASKING QUESTIONS OF THE ENVIRONMENT... the Spring Environment abstraction helps. where am I? Friday, September 20, 13 @Inject Environment env; String whereAmI = env.getProperty(“user.dir”);
  17. 17. ASKING QUESTIONS OF THE ENVIRONMENT... the Spring Environment abstraction helps. who are you? Friday, September 20, 13 @Inject Environment env; String whoAreYou = env.getProperty(“database.url”);
  18. 18. BUILDING ADAPTIVE APPLICATIONS IS HARD Demo using the Environment Friday, September 20, 13
  19. 19. ASKING QUESTIONS OF THE ENVIRONMENT... Values may come from anywhere. You can add your own PropertySource implementations, too. ApplicationContext context = ... ConfigurableEnvironment environment = (ConfigurableEnvironment) context.getEnvironment(); Map<String, Object> map = new HashMap<String, Object>(); map.put(“value1”, “url”); MapPropertySource mapPropertySource = new MapPropertySource( "dbConfig", map); environment.getPropertySources().addFirst( mapPropertySource ); Friday, September 20, 13
  20. 20. BUILDING ADAPTIVE APPLICATIONS IS HARD for convenience, just use the @PropertySource annotation @PropertySource("/config.properties") @Configuration public class ServiceConfiguration { Friday, September 20, 13
  21. 21. BUILDING ADAPTIVE APPLICATIONS IS HARD then you can use the Environment in your Java configuration... @PropertySource("/config.properties") @Configuration public class ServiceConfiguration { @Bean public DataSource database(Environment env) { try { String user = env.getProperty(“db.user”), pw = env.getProperty(“db.password”), url = env.getProperty(“db.url”); Class<? extends Driver> driverClass = env.getPropertyAsClass( “db.driverClass”, Driver.class); Friday, September 20, 13
  22. 22. BUILDING ADAPTIVE APPLICATIONS IS HARD Demo using @PropertySource Friday, September 20, 13
  23. 23. PROFILES Multiple Implementations Allow you to provide one implementation for production and other versions that may not really work... Friday, September 20, 13
  24. 24. PROFILES Mocking for Testing Mock anything that is non-predictive slow Testing Edge conditions what-if scenarios Friday, September 20, 13
  25. 25. PROFILES Mocking for Productivity Services are not always available Services are often slow Start the front-end when you start the back-end Friday, September 20, 13
  26. 26. PROFILES @Service public class VideoSearchYoutube implements VideoSearch { public List<String> lookupVideo(String searchTerm) throws Exception{ ... return results; @Service public class VideoSearchMock implements VideoSearch { public List<String> lookupVideo(String searchTerm) throws Exception{ ... return results; Friday, September 20, 13
  27. 27. PROFILES Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [savetheenvironment.embedded.video.VideoSearch] is defined: expected single matching bean but found 2: videoSearchMock,videoSearchYouTube Friday, September 20, 13
  28. 28. BUILDING ADAPTIVE APPLICATIONS IS HARD no more external .xml contexts! Friday, September 20, 13
  29. 29. PROBLEM WITH ANNOTATIONS @Profile(“VideoYoutube”) @Service public class VideoSearchYoutube implements VideoSearch { public List<String> lookupVideo(String searchTerm) throws Exception{ ... return results; @Profile(“VideoMock”) @Service public class VideoSearchMock implements VideoSearch { public List<String> lookupVideo(String searchTerm) throws Exception{ ... return results; Friday, September 20, 13
  30. 30. BUILDING ADAPTIVE APPLICATIONS IS HARD you can also use profiles in your unit tests. @ContextConfiguration(classes = {ServiceConfiguration.class}) @ActiveProfiles(“mock”) @RunWith(SpringJUnit4ClassRunner.class) public class VideoSearchTest { @Inject private VideoSearch videoSearch; @Test public void testVideoSearch() throws Exception { assertEquals( 2, videoSearch.lookupVideo("Kevin Nilson").size()); } .. Friday, September 20, 13
  31. 31. ADAPTIVE SPRING Demo using @Profile Friday, September 20, 13
  32. 32. BUILDING ADAPTIVE APPLICATIONS IS HARD profiles tag groups of beans into sets dev dataSource txManager mongo Friday, September 20, 13 cloud dataSource txManager mongo qa dataSource txManager mongo
  33. 33. BUILDING ADAPTIVE APPLICATIONS IS HARD the Environment also offers access to “profiles.” profiles let you tag groups of beans, and enable them selectively. Friday, September 20, 13
  34. 34. BUILDING ADAPTIVE APPLICATIONS IS HARD It’s easy to tag a bean with a profile in Java configuration @Configuration @Profile(“dev”) class LocalConfiguration { } @Configuration @Profile(“production”) class ProductionConfiguration { } Friday, September 20, 13
  35. 35. BUILDING ADAPTIVE APPLICATIONS IS HARD It’s easy to tag a bean with a profile on a bean-by-bean basis @Component @Profile(“dev”) class InMemoryCustomerRepository implements CustomerRepository { // ... } @Component @Profile(“production”) class JdbcCustomerRepository implements CustomerRepository { // ... } Friday, September 20, 13
  36. 36. BUILDING ADAPTIVE APPLICATIONS IS HARD You specify a profile on the ApplicationContext’s Environment ApplicationContext ac = .. Environment env = ac.getEnvironment(); env.setActiveProfiles( production ? “production” : “dev” ); ac.register(ServiceConfiguration.class); ac.refresh(); Friday, September 20, 13
  37. 37. BUILDING ADAPTIVE APPLICATIONS IS HARD You specify a profile with a system environment variable -Dspring.profiles.active=dev,qa Friday, September 20, 13
  38. 38. BUILDING ADAPTIVE APPLICATIONS IS HARD You specify a profile with a init-param or context-param in web.xml <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>spring.profiles.active</param-name> <param-value>qa,dev</param-value> </init-param> </servlet> Friday, September 20, 13
  39. 39. BUILDING ADAPTIVE APPLICATIONS IS HARD You specify a profile in code in a Servlet (web.xml) environment with an ApplicationContextInitializer: public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { public void initialize(ConfigurableApplicationContext applicationContext) { } String p = (Math.random() > .5 ? "production" : "qa"); applicationContext.getEnvironment().setActiveProfile(p); } Friday, September 20, 13
  40. 40. BUILDING ADAPTIVE APPLICATIONS IS HARD Then simply register it with the DispatcherServlet. It’ll be invoked before any beans are read and configured. <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextInitializerClasses</param-name> <param-value> app.MyApplicationContextInitializer </param-value> </init-param> </servlet> Friday, September 20, 13
  41. 41. BUILDING ADAPTIVE APPLICATIONS IS HARD class CrmAbstractDispatcherServletInitializer extends AbstractDispatcherServletInitializer { it’s even simpler with Servlet 3 environments: @Override protected WebApplicationContext createServletApplicationContext() { boolean production = // value is presumably obtained externally AnnotationConfigWebApplicationContext ac = .. ac.getEnvironment().setActiveProfiles( production ? "production" : "dev" ); ac.register(WebMvcConfiguration.class); ac.refresh(); return ac; } @Override protected String[] getServletMappings() { return new String[]{"/*"}; } Friday, September 20, 13 } @Override protected WebApplicationContext createRootApplicationContext() { return null; }
  42. 42. BUILDING ADAPTIVE APPLICATIONS IS HARD You specify a profile for Spring Boot applications in the application.properties file $>cat src/main/resources/application.properties # Spring Boot will read this file spring.profiles.active=dev,qa Friday, September 20, 13
  43. 43. BUILDING ADAPTIVE APPLICATIONS IS HARD Some profiles are special (in certain contexts). (we’re all special in certain contexts!) default is activated... by default. Cloud Foundry activates the cloud profile if it’s available. Friday, September 20, 13
  44. 44. BUILDING ADAPTIVE APPLICATIONS IS HARD Demo to Configure Profiles using EC2 Tags http://tinyurl.com/adaptive-spring-boot Friday, September 20, 13
  45. 45. REST DESIGN WITH SPRING An example of using profiles in EC2 Specific Instance ec2-describe-tags --filter "resource-id=i-6e8be600 --filter "key=profile" Current Instance profile=$(ec2-describe-tags --filter "resource-id=$(ec2-metadata -i | cut -d ' ' -f2)" --filter "key=profile" | cut -f5) Start Jetty java -Dprofile=$profile -jar start.jar java -Dspring.profiles.active=bacon -jar start.jar Friday, September 20, 13
  46. 46. ADAPTING THE SYSTEM AT CONFIGURATION TIME The wide world of @Conditional in Spring 4 Friday, September 20, 13
  47. 47. BUILDING ADAPTIVE APPLICATIONS IS HARD @Profile are specializations of an annotation called @Conditional. Sort of. Friday, September 20, 13
  48. 48. BUILDING ADAPTIVE APPLICATIONS IS HARD @Conditional lets you conditionally create beans. They delegate to Condition instances. Supports graceful, resilient systems. Friday, September 20, 13
  49. 49. BUILDING ADAPTIVE APPLICATIONS IS HARD Spring Boot uses @Conditional to great effect. Provides a healthy suite of Condition implementations like DatabaseCondition, OnBeanCondition, OnClassCondition, and OnWebApplicationCondition. You can write your own, too. Friday, September 20, 13
  50. 50. BUILDING ADAPTIVE APPLICATIONS IS HARD Demo Conditions, looking at existing writing your own Condition Friday, September 20, 13
  51. 51. ADAPTING THE SYSTEM AT CONFIGURATION TIME Refreshing beans with ApplicationContext child contexts Friday, September 20, 13
  52. 52. BUILDING ADAPTIVE APPLICATIONS IS HARD ApplicationContext’s are hierarchical. They nest. You see this effect when you setup the typical Spring MVC architecture: ContextLoaderListener DispatcherServlet DispatcherServlet Friday, September 20, 13 DispatcherServlet
  53. 53. BUILDING ADAPTIVE APPLICATIONS IS HARD You can programmatically parent an ApplicationContext, created isolated access to the parent. You can also isolate and destroy just parts of an ApplicationContext hierarchy, too. Friday, September 20, 13
  54. 54. BUILDING ADAPTIVE APPLICATIONS IS HARD Demo pt. 1 “reloading” beans, Friday, September 20, 13
  55. 55. ADAPTING THE SYSTEM AT CONFIGURATION TIME better decoupling with ApplicationEvents Friday, September 20, 13
  56. 56. BUILDING ADAPTIVE APPLICATIONS IS HARD the Spring ApplicationContext supports a service bus of sorts. You may publish and receive messages from one component to another using ApplicationEvents. Friday, September 20, 13
  57. 57. ADAPTING THE SYSTEM AT CONFIGURATION TIME Refreshing beans with proxies Friday, September 20, 13
  58. 58. BUILDING ADAPTIVE APPLICATIONS IS HARD Another approach to refreshing a bean: 1. Use bean proxies to isolate reference holders from the swap. 2. Use ApplicationEvent to trigger the refresh of the bean. 3. Let the client provide the factory method. Friday, September 20, 13
  59. 59. BUILDING ADAPTIVE APPLICATIONS IS HARD Demo pt. 2 “reloading” beans, Friday, September 20, 13
  60. 60. ADAPTING THE SYSTEM AT CONFIGURATION TIME Refreshing beans with the JMX (and proxies) Friday, September 20, 13
  61. 61. BUILDING ADAPTIVE APPLICATIONS IS HARD Spring provides the @EnableMBeanExport configuration to easily expose beans through JMX. @ManagedResource public class JmxDataSourceConfigurationEndpoint implements ApplicationContextAware { private DataSource dataSource; private ConfigurableEnvironment environment; private ApplicationContext applicationContext; @ManagedOperation public void updateDataSource(String user, String pw, String url) { Map<String, Object> map = ... MapPropertySource propertySource = ... // install the new configuration values environment.getPropertySources().addFirst(propertySource); // publish the event that our proxies will listen for applicationContext.publishEvent( new RefreshEvent( this.dataSource)); } Friday, September 20, 13 }
  62. 62. BUILDING ADAPTIVE APPLICATIONS IS HARD Demo pt. 3 “reloading” beans, Friday, September 20, 13
  63. 63. REST DESIGN WITH SPRING Any ? Questions @starbuxman | jlong@gopivotal.com | http://slideshare.net/joshlong @javaclimber | kevin@javaclimber.com | http://www.javaclimber.com Friday, September 20, 13

×