adaptive spring applications with profiles and more
DESCRIPTION
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.TRANSCRIPT
© 2013 SpringOne 2GX. All rights reserved. Do not distribute without permission.
Adaptive Spring Applications with Profiles and More
by Josh Long and Kevin Nilson
Friday, September 20, 13
Developer Advocateinterested in big data, the open web, cloud and all things Spring
2
About Josh Long
Friday, September 20, 13
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
WELCOME
Welcome
Friday, September 20, 13
ADAPTING THE SYSTEM AT CONFIGURATION TIME
Adapting Beans at Construction Time
Friday, September 20, 13
TAILORING BEAN CONSTRUCTION
manages the life cycle of beans in the ApplicationContext
<bean init-method="init" class="some.Bean" destroy-method="destroy"> ... </bean>
package some;
public class Bean { public void init(){ } public void destroy(){ }}
Friday, September 20, 13
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
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
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
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
TAILORING BEAN CONSTRUCTION
@PropertySource("/config.properties")@Configurationpublic class ServiceConfiguration {
@Bean public DataSource postgres(Environment e) { // ... }
@Bean public CustomerService customerService(DataSource ds) { return new CustomerService(ds); }}
but with Java configuration, you don’t need the indirection anymore..
Friday, September 20, 13
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
java.util.concurrent.Callable<DataSource> dataSourceCallable ...?and what about..
Friday, September 20, 13
TAILORING BEAN CONSTRUCTION
factory methods are going to be awesome with Java 8
Provider<DataSource> dataSourceProvider = ()->{ // ...};
Friday, September 20, 13
ADAPTING THE SYSTEM AT CONFIGURATION TIME
The Environment and Profiles
Friday, September 20, 13
ASKING QUESTIONS OF THE ENVIRONMENT...
the environment in which an application runs isn’t always certain...
Friday, September 20, 13
ASKING QUESTIONS OF THE ENVIRONMENT...
the Spring Environment abstraction helps.
am I?where
@Inject Environment env;
String whereAmI = env.getProperty(“user.dir”);
Friday, September 20, 13
ASKING QUESTIONS OF THE ENVIRONMENT...
the Spring Environment abstraction helps.
@Inject Environment env;
String whoAreYou = env.getProperty(“database.url”);
are who
you?
Friday, September 20, 13
BUILDING ADAPTIVE APPLICATIONS IS HARD
Demousing the Environment
Friday, September 20, 13
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
BUILDING ADAPTIVE APPLICATIONS IS HARD
for convenience, just use the @PropertySource annotation
@PropertySource("/config.properties")@Configurationpublic class ServiceConfiguration {
Friday, September 20, 13
BUILDING ADAPTIVE APPLICATIONS IS HARD
then you can use the Environment in your Java configuration...
@PropertySource("/config.properties")@Configurationpublic 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
BUILDING ADAPTIVE APPLICATIONS IS HARD
Demousing @PropertySource
Friday, September 20, 13
PROFILES
Multiple ImplementationsAllow you to provide one implementation for production and other versions that may not really work...
Friday, September 20, 13
PROFILES
Mocking for TestingMock anything that is non-predictive slow
Testing Edge conditions what-if scenarios
Friday, September 20, 13
PROFILES
Mocking for ProductivityServices are not always available
Services are often slow
Start the front-end when you start the back-end
Friday, September 20, 13
PROFILES
@Servicepublic class VideoSearchYoutube implements VideoSearch { public List<String> lookupVideo(String searchTerm) throws Exception{ ... return results;
@Servicepublic class VideoSearchMock implements VideoSearch { public List<String> lookupVideo(String searchTerm) throws Exception{ ... return results;
Friday, September 20, 13
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
BUILDING ADAPTIVE APPLICATIONS IS HARD
no more external .xml contexts!
Friday, September 20, 13
PROBLEM WITH ANNOTATIONS
@Profile(“VideoYoutube”)@Servicepublic class VideoSearchYoutube implements VideoSearch { public List<String> lookupVideo(String searchTerm) throws Exception{ ... return results;
@Profile(“VideoMock”)@Servicepublic class VideoSearchMock implements VideoSearch { public List<String> lookupVideo(String searchTerm) throws Exception{ ... return results;
Friday, September 20, 13
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
ADAPTIVE SPRING
Demousing @Profile
Friday, September 20, 13
BUILDING ADAPTIVE APPLICATIONS IS HARD
profiles tag groups of beans into sets
dev cloud qa
dataSource
txManager
mongo
dataSource
txManager
mongo
dataSource
txManager
mongo
Friday, September 20, 13
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
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
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
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
BUILDING ADAPTIVE APPLICATIONS IS HARD
You specify a profile with a system environment variable
-Dspring.profiles.active=dev,qa
Friday, September 20, 13
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
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
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
BUILDING ADAPTIVE APPLICATIONS IS HARD
it’s even simpler with Servlet 3 environments:
class CrmAbstractDispatcherServletInitializer extends AbstractDispatcherServletInitializer {
@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[]{"/*"}; }
@Override protected WebApplicationContext createRootApplicationContext() { return null; }}Friday, September 20, 13
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 filespring.profiles.active=dev,qa
Friday, September 20, 13
BUILDING ADAPTIVE APPLICATIONS IS HARD
Some profiles are special (in certain contexts).
default is activated... by default.
Cloud Foundry activates the cloud profile if it’s available.
(we’re all special in certain contexts!)
Friday, September 20, 13
BUILDING ADAPTIVE APPLICATIONS IS HARD
Demousing EC2 Tags to Configure Profiles
http://tinyurl.com/adaptive-spring-boot
Friday, September 20, 13
REST DESIGN WITH SPRING
An example of using profiles in EC2
Specific Instanceec2-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 Jettyjava -Dprofile=$profile -jar start.jar
java -Dspring.profiles.active=bacon -jar start.jar
Friday, September 20, 13
ADAPTING THE SYSTEM AT CONFIGURATION TIME
The wide world of @Conditional in Spring 4
Friday, September 20, 13
BUILDING ADAPTIVE APPLICATIONS IS HARD
@Profile are specializations of an annotation called @Conditional.
Sort of.
Friday, September 20, 13
BUILDING ADAPTIVE APPLICATIONS IS HARD
@Conditional lets you conditionally create beans.
They delegate to Condition instances.
Supports graceful, resilient systems.
Friday, September 20, 13
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
BUILDING ADAPTIVE APPLICATIONS IS HARD
Demolooking at existing Conditions,writing your own Condition
Friday, September 20, 13
ADAPTING THE SYSTEM AT CONFIGURATION TIME
Refreshing beans with ApplicationContext child contexts
Friday, September 20, 13
BUILDING ADAPTIVE APPLICATIONS IS HARD
ApplicationContext’s are hierarchical. They nest.
You see this effect when you setup the typical Spring MVC architecture:
ContextLoaderListener
DispatcherServletDispatcherServlet
DispatcherServlet
Friday, September 20, 13
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
BUILDING ADAPTIVE APPLICATIONS IS HARD
Demo“reloading” beans, pt. 1
Friday, September 20, 13
ADAPTING THE SYSTEM AT CONFIGURATION TIME
better decoupling with ApplicationEvents
Friday, September 20, 13
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
ADAPTING THE SYSTEM AT CONFIGURATION TIME
Refreshing beans with proxies
Friday, September 20, 13
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
BUILDING ADAPTIVE APPLICATIONS IS HARD
Demo“reloading” beans, pt. 2
Friday, September 20, 13
ADAPTING THE SYSTEM AT CONFIGURATION TIME
Refreshing beans with the JMX (and proxies)
Friday, September 20, 13
BUILDING ADAPTIVE APPLICATIONS IS HARD
Spring provides the @EnableMBeanExport configuration to easily expose beans through JMX.
@ManagedResourcepublic 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
BUILDING ADAPTIVE APPLICATIONS IS HARD
Demo“reloading” beans, pt. 3
Friday, September 20, 13
REST DESIGN WITH SPRING
Any
Questions?@starbuxman | [email protected] | http://slideshare.net/joshlong
@javaclimber | [email protected] | http://www.javaclimber.com
Friday, September 20, 13