Lunch and Learn

May 6, 2015

Reid Harrison

  • Apache Struts -> Spring MVC
  • Gin & Guice and Spring
  • Security
  • JDBC
  • Android
  • Social
  • OAuth2
  • XD
  • Batch
  • Integration
  • Data
  • Test

2013

Personal Mobile Project

 

 

 

2014

Unified Presentation Framework

2010

Georgia Tech Research Institute

 

 

 

2012

Personal Web Project

 

 

My Spring History

"Spring helps development teams everywhere build simple, portable,

fast and flexible JVM-based systems and applications."

Rod Johnson

Juergen Hoeller

Mark Pollack

Mark Fisher

What is Spring?

  • Spring Documentation
    • Dense but detailed
    • Often hard to understand
  • Tutorials
    • Often outdated
    • Use in conjunction with docs and other tuts
  • Examples
    • Good starting point
    • Numerous but simple
  • Exploration
    • Figure it out for yourself
    • Become familiar with internals

Learning Spring

@Repository
public class PeopleDao extends JdbcDaoSupport {

    @InjectLogger
    private Logger logger;

    @Inject
    public PeopleDao(final DataSource dataSource) {
        setDataSource(dataSource);
    }
}


@Configuration
public class SpringConfig {

    @Bean
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl("jdbc:postgresql://localhost:5432/tdpapp");
        dataSource.setUsername("username");
        dataSource.setPassword("password");
        return dataSource;
    }
}

Spring Dependency Injection

@Controller
public class HelloController {

    @RequestMapping(value = "/")
    public ModelAndView home() {
        ModelAndView mav = new ModelAndView("index");
        String greeting = "Hello, World!";
        mav.addObject("greeting", greeting);
        return mav;
    }

    @RequestMapping(value = "helloWorld", method = RequestMethod.GET)
    public @ResponseBody
    String helloWorld(final String greeting) {
        if ("Hello!".equals(greeting)) {
            return "Hello, World!";
        }

        return "Hell no, World!";
    }
}

Spring MVC

@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurrentUser { }
public class ActiveUserWebArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        Annotation[] paramAnns = methodParameter.getParameterAnnotations();
        for (Annotation paramAnn : paramAnns) {
            if (CurrentUser.class.isInstance(paramAnn)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        Principal principal = nativeWebRequest.getUserPrincipal();
        if (principal == null) {
            return null;
        }
        User springUser = (User) ((Authentication) principal).getPrincipal();
    }
}

Java Annotations

Java Annotations

@Controller
public class AppController {
    
    @RequestMapping("getNotificationCount")
    public int getNotificationCount(@CurrentUser User currentUser) {
        return service.getUserNotificationCount(currentUser.getUsername());
    }

}
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Inject
    private DataSource userDataSource;

    @Override
    public void configure(AuthenticationManagerBuilder authBuilder) throws Exception {
        authBuilder
            .jdbcAuthentication()
                .dataSource(userDataSource)
                .usersByUsernameQuery(USER_BY_USERNAME_QUERY)
                .authoritiesByUsernameQuery(AUTHORITIES_BY_USERNAME_QUERY);
    }
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .disable()
            .authorizeRequests()
                .antMatchers("/css/**", "/img/**", "/js/**").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("index").permitAll()
                .loginProcessingUrl("login").permitAll()
                .successHandler(new CustomAuthenticationSuccessHandler())
                .failureHandler(new CustomAuthenticationFailureHandler())
                .and()
            .logout()
                .logoutUrl("logout")
                .logoutSuccessUrl("index").permitAll();
    }
}

Spring Security

@Controller
public class AppController {

    @Secured({"ADMIN"})
    @RequestMapping(value="admin")
    public String admin() {
        return "admin";
    }

    @PreAuthorize("#username == authentication.name")
    @RequestMapping(value="u/{username}/edit", method=RequestMethod.GET)
    public ModelAndView editUser(@PathVariable String username) {
        ModelAndView mav = new ModelAndView("editUser");
        mav.addObject("user", service.getUserInfo(username);
        return mav;
    }
}

Spring Security

@Repository
public class PeopleDao extends JdbcDaoSupport {

    @Inject
    public PeopleDao(final DataSource dataSource) {
        setDataSource(dataSource);
    }

    . . .

    public List<Expertise> getExpertise(final String eid) {
        return getJdbcTemplate().query(SELECT_PERSON_EXPERTISE_SQL, new String[] { eid }, new PeopleRowMappers.ExpertiseRowMapper());
    }

    public Integer createExpertise(final String newExpertiseName) {
        //Construct a SimpleJdbcInsert so that the new expertise's key can be returned from the database
        SimpleJdbcInsert insert = new SimpleJdbcInsert(getJdbcTemplate())
                .withTableName("expertise")
                .usingColumns("name")
                .usingGeneratedKeyColumns("id");

        Map<String, Object> expertiseValues = new TreeMap<String, Object>();
        expertiseValues.put("name", newExpertiseName);

        //Execute the insert and capture the returned primary key
        final Number newExpertiseKey = insert.executeAndReturnKey(expertiseValues);

        if (newExpertiseKey == null) {
            throw new IllegalStateException("New expertise's primary key was not returned by database after insertion, " +
                    "the new expertise will not be able to be related to a person.");
        }

        final int newExpertiseId = newExpertiseKey.intValue();
        return newExpertiseId;
    }
}

Spring JDBC

public void updatePersonSchool(final String eid, final Integer schoolId) {
    getJdbcTemplate().update(UPDATE_PERSON_SCHOOL_SQL, new PreparedStatementSetter() {
        @Override
        public void setValues(PreparedStatement ps) throws SQLException {
            if (schoolId != null) {
                ps.setInt(1, schoolId);
            } else {
                ps.setNull(1, Types.INTEGER);
            }
    
            ps.setString(2, eid);
        }
    });
}

Spring JDBC

@Controller
public class TwitterFriendsController {

    private final Twitter twitter;
	
    @Inject
    public TwitterFriendsController(Twitter twitter) {
        this.twitter = twitter;
    }
	
    @RequestMapping(value="/twitter/friends", method=RequestMethod.GET)
    public String friends(Model model) {
        model.addAttribute("profiles", twitter.friendOperations().getFriends());
        return "twitter/friends";
    }

    @RequestMapping(value="/twitter/followers", method=RequestMethod.GET)
    public String followers(Model model) {
        model.addAttribute("profiles", twitter.friendOperations().getFollowers());
        return "twitter/friends";
    }

}

Spring Social

Spring Social

@Configuration
@EnableSocial
public class SocialConfig implements SocialConfigurer {

    @Override
    public void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {
        cfConfig.addConnectionFactory(new TwitterConnectionFactory(
            env.getProperty("twitter.consumerKey"),
            env.getProperty("twitter.consumerSecret")));
    }

    @Bean
    @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
    public Twitter twitter(ConnectionRepository repository) {
        Connection<Twitter> connection = repository.findPrimaryConnection(Twitter.class);
        return connection != null ? connection.getApi() : null;
    }
}

Spring Social

Spring Boot

@RestController
@EnableAutoConfiguration
public class AppController {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(AppController.class, args);
    }

}

Spring Boot

HTTP

Tail

File

Mail

Twitter

TCP/UDP

JMS

RabbitMQ

Trigger

Filter

Transformer

Splitter

Aggregator

Object-to-JSON

JSON-to-Tuple

HTTP Client

Shell

File

HDFS

JDBC

MongoDB

TCP

Log

Mail

Splunk

Source

Processor

(optional)

Sink

Spring XD

source | processor (optional) | sink --parameter=value

stream create --name mongoperf --definition "jms 
--provider=perftest --destination='epf.audit.queue' | 
audit-json | mongodb --host='10.94.71.148' --port=11200 
--databaseName=upfaudit"
stream create dfs-http --definition "http | 
file --dir=C:\Users\gtb012\Desktop\ --name=dfs-http-test 
--suffix=txt" --deploy

Spring XD

Spring XD

  • are managed by Spring's IoC container - ApplicationContext
  • are declared through XML or Java
  • must be non-final and non-static
  • usually need default or injection constructors

Beans

can then be injected into instance variables, constructor parameters or setter methods.

More Spring DI

  • @Inject vs. @Autowired
  • @Named vs. @Qualifier
  • @Component
    • @Controller
    • @Service
    • @Repository
  • @Scope
    • singleton
    • prototype
    • request
    • session
  • @Profile

Common Spring Annotations

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
	    http://www.springframework.org/schema/beans
	    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <!-- component scan for Spring Config -->
    <context:component-scan base-package="com.capitalone.tdp.tdpapp.server.web.spring"
                            use-default-filters="false">
        <context:include-filter type="annotation"
                                expression="org.springframework.context.annotation.Configuration" />
    </context:component-scan>
    
    <!-- PostgreSQL data source bean defined as XML -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.postgresql.Driver"/>
        <property name="url" value="jdbc:postgresql://localhost:5432/tdpapp"/>
        <property name="username" value="username"/>
        <property name="password" value="password"/>
    </bean>

</beans>

Application Context

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {
        "com.capitalone.tdp.tdpapp.server",
        "com.capitalone.epf.logging",
        "com.capitalone.epf.configuration.utils",
        "com.capitalone.epf.core.jar.springconfig"
})
public class TdpAppSpringConfig {

    @Inject
    @Named("upf-app-config")
    org.apache.commons.configuration.Configuration upfAppConfig;

    @Bean
    public DataSource dataSource() throws Exception {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl(upfAppConfig.getString("postgres.url"));
        dataSource.setUsername(upfAppConfig.getString("postgres.username"));
        dataSource.setPassword(upfAppConfig.getString("postgres.password"));
        return dataSource;
    }

    @Bean
    @Named("peopleDataSource")
    @Scope("request")
    public DataSource peopleDataSource() throws Exception {
        . . .
    }

    @Bean(name = {"peopleDao"})
    @Inject
    public PeopleDao peopleDao(@Named("peopleDataSource") dataSource) {
        return new PeopleDao(dataSource);
    }
    
}

Java @Configuration

@Repository
public class PeopleDao extends JdbcDaoSupport {

    @InjectLogger
    private Logger logger;

    @Inject
    public PeopleDao(final DataSource dataSource) {
        setDataSource(dataSource);
    }

    . . .
}
@Service
public class PeopleService {

    @InjectLogger
    private Logger logger;

    @Inject
    private PeopleDao peopleDao;

    public List<Person> getAllPeople() {
        return peopleDao.getAllPeople();
    }
}

Spring DI

More Spring MVC

public class HelloServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("application/json");
        PrintWriter out = response.getWriter();
        out.println("{\"greeting\":\"Hello, World!\"}");
        out.close();
    }
}
<servlet>
   <servlet-name>HelloServlet</servlet-name>
   <servlet-class>HelloServlet</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>HelloServlet</servlet-name>
   <url-pattern>/helloWorld</url-pattern>
</servlet-mapping>

Dispatcher Servlet

web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app id="tdp-app-server" version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <display-name>tdp-app-server</display-name>

    <servlet>
        <servlet-name>tdp-app-server</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/tdp-app-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>tdp-app-server</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.1.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    <!-- Configures a default handler serving static resources and the default "/" mapping to the DispatcherServlet. -->
    <mvc:default-servlet-handler />

    <!-- Activates annotation-based bean configuration. -->
    <context:annotation-config />

    <!-- Configures the @Controller programming model. -->
    <mvc:annotation-driven />

    <!-- The servlet's main Spring Controller -->
    <context:component-scan base-package="com.capitalone.tdp.tdpapp.server.web.controller" />

</beans>
servlet.xml
@Controller
public class HelloController {

    @Inject
    private Service service;

    @RequestMapping(value = "helloWorld", method = RequestMethod.GET)
    public @ResponseBody
    List<HelloWorld> helloWorld(final String greeting) {
        return service.getHellos();
    }

    @RequestMapping(value="u/{username}/edit", method=RequestMethod.POST)
    public String editUser(@PathVariable String username, @RequestBody User newUser) {
        service.editUser(username, newUser);
        return "edit_user_success";
    }
}

@Controller

@Configuration
public class AppStringConfig {

    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    @Bean
    public VelocityLayoutViewResolver viewResolverVelocity() {
        VelocityLayoutViewResolver resolver = new VelocityLayoutViewResolver();
        resolver.setCache(true);
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".vm");
        resolver.setRequestContextAttribute("requestContext");
        resolver.setExposeSpringMacroHelpers(true);
        resolver.setToolboxConfigLocation("/WEB-INF/vm/config/velocityTools.xml");
        resolver.setLayoutUrl("/WEB-INF/vm/web/layout/layout.vm");
        return resolver;
    }
}

View Resolvers

  • Spring Documentation
  • GitHub
    • spring-projects/spring-mvc-showcase
    • spring-projects/spring-xd-samples
    • spring-projects/spring-boot/tree/master/spring-boot-samples
  • StackOverflow
  • questions.kdc.capitalone.com

Spring Resources

deck

By Reid Harrison

deck

  • 1,030