jDays2015 - JavaEE vs. Spring Smackdown
-
Upload
mert-caliskan -
Category
Software
-
view
604 -
download
1
Transcript of jDays2015 - JavaEE vs. Spring Smackdown
Java EE vs. Spring Smackdown
Mert Çalışkan, @mertcalMurat Yener, @yenerm
• Murat Yener
• 10+ Years of Java & Mobile
• Coder @ intel.com
• Google Developer Expert (Android)
• Profile available @ tr.linkedin.com/in/muratyener
• @yenerm
The Fighters
• Mert Çalışkan
• 10+ Years of Enterprise Java
• Coder @ t2.com.tr
• Oracle Java Champion
• Profile available @ tr.linkedin.com/in/mertcaliskan
• @mertcal
• Murat Yener
• 10+ Years of Java & Mobile
• Coder @ intel.com
• Google Developer Expert (Android)
• Profile available @ tr.linkedin.com/in/muratyener
• @yenerm
The Fighters
• Mert Çalışkan
• 10+ Years of Enterprise Java
• Coder @ t2.com.tr
• Oracle Java Champion
• Profile available @ tr.linkedin.com/in/mertcaliskan
• @mertcal
The Books that started it all…
http://amazon.com/author/murat http://amazon.com/author/mert
or DO YOU NEED SPECIAL ABILITIES?IS JAVA EE ENOUGH?
Come to the Spring Side..!
Current Trend on Java EE and Spring Framework
http://www.google.com/trends/explore#q=Java%20EE%2C%20Spring%20Framework
in 2 versions, one with Java EE 7 and one with Spring 4!
So we created a Sample Application named
No project, server, framework or even a POJO has been harmed during the preparation on this talk.
Still we’re professionals so don’t try this at home (but you may do try at work…)
Disclaimer
This talk is fiction, we are not messengers of either platform. We both used each framework in a project at some point in our lifes
and we reserve to switch sides at any time in future.
Still for the sake of our book sells, we encourage you to pick the best side and stick with it forever!
Disclaimer II
•Persistence
•CRUD
• Services
• REST
• Patterns
Simple Domain
FighterSchedule
ScoreBoard
Gameschedules
has
has
What is Fight Club?
J2EE
• Complete redesign of Enterprise Java
• Based on Spring
• Java EE and the Ewoks
Java EE
Spring
• Started with aim for simplifying J2EE
• Based on POJO programming model
• Offered DI instead of JNDI
• Enabled use of lightweight web servers instead of heavy J2EE server
JavaEE7 Spring
CDI Spring IoC
Interceptor Spring AOP AspectJ
JPA Persistence
Dependency Injection
AOP
JPA JDBC
UIJSF 2 Spring MVC JSF 2
WSJAX-WSJAX-RS Spring MVCREST Support
Security Spring Security
Contract-first SOAP WS
TestingN/A Spring Testing
Java EE Security
EJB
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.1.5.RELEASE</version></dependency><dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>4.1.5.RELEASE</version></dependency>
<dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope></dependency>
} expressionbeanscontext coreweb
Maven as Dependency Manager
JavaEE7
Spring
webmvc
aop }
Setup
IDE: NetBeans 8.0.2 Intellij IDEA 14 Ultimate
Server: Payara Payara
On IDEA, Mert needed to register glassfish-resources.xml file with command: ./asadmin add-resources ~/path-to-file/glassfish-resources.xmland then start derby DB with ./asadmin start-database and then connect to the sample Db with ij.sh as: ij> connect 'jdbc:derby://localhost:1527/sample;create=true’
SpringJavaEE
‘0’ config
Configuration
• Context configuration file (either as an XML file or a Java class) is needed to bootstrap the application.
• With Spring MVC, the location of the configuration file is provided to the Dispatcher Servlet definition in web.xml.
• It’s also possible to provide multiple configuration files and they will be merged prior to context initialization.
SpringJavaEE
Java EE: bootstrapping
<beans/>
An empty beans.xml was needed to start the CDI container..not any more
Spring: bootstrapping - 1<?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:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <context:component-scan base-package="com.devchronicles"/> <context:annotation-config/> <mvc:annotation-driven/> <tx:annotation-driven/> <task:annotation-driven/> <tx:jta-transaction-manager/> <aop:aspectj-autoproxy/>
Spring: bootstrapping - 2 - con’td
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"/> <property name="suffix" value=".jsp"/> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="persistenceUnitName" value="fightclub-jpa"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true"/> </bean> </property> </bean> <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="fightclub"/> </bean> </beans>
More configuration to come for security & testing…
JavaEE's web.xml
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>facesServlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>facesServlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> </web-app>
Spring’s web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>FightClub Code Demonstration</display-name> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping> </web-app>
Bean Declaration
JavaEE7 Spring@Stateless@Path("fighter") public class FighterService { @PersistenceContext private EntityManager entityManager; @Inject Event<Fighter> event; @POST @Consumes({"application/xml", "application/json"}) public void create(Fighter fighter) { entityManager.persist(fighter); event.fire(fighter); }}
@RestController@RequestMapping(value = "/fighters") public class FighterRestController { @PersistenceContext private EntityManager entityManager; @RequestMapping(value = "/create", method = RequestMethod.POST) public void create(@RequestBody Fighter fighter) { entityManager.persist(fighter); } }
• @Namedderivatives: @Stateless, @Stateful
• @Inject, @EJB, @Resource
• @Alternative
• @PostConstruct, @PreDestroy
• @Dependent@Request@ConversationScoped@SessionScoped@ApplicationScoped
• @Produces
Annotations
• @Componentderivatives: @Service, @Controller, @Repository
• @Autowired
• @Primary
• @PostConstruct, @PreDestroy
• @Scope(BeanDefinition.SCOPE_PROTOTYPE) @Scope(“request”) N/A (w/ Spring WebFlow) @Scope(“session”) @Scope(BeanDefinition.SCOPE_SINGLETON)
• @Configuration + @Bean
SpringJavaEE7
Design Patterns
@Singleton
@Observes
@Produces
@Decorator + XML (for order)
@Interceptor
@Inject++
@Asynchronous, Servlet 3.0+, Websockets
@Schedule
@Component and its derivatives
N/A, github: spring-event-annotations
@Configuration + @Bean
N/A, github: spring-decorator
@Aspect, @Around, @After, @AfterReturning, @AfterThrowing, @Before, @Pointcut
@Autowired
@Async
@Scheduled
Singleton
Observer
Factory
Decorator
AOP
DependencyInjection
AsynchronousProgramming
Timer
SpringJavaEE7
some code
UI
• JSF
• Component Based Framework
• Using CDI beans as backing beans
• FacesServlet configuration
• Others…
Angular + REST
GWT/Vaadin + CDI
• Spring MVC
• Action Based Framework
• Controllers and REST services to feed JSP files and jQuery could be used (heavily…)
• DispatcherServlet configuraion
• Others…
Angular + REST
JSF, Vaadin, GWT
SpringJavaEE7
Java EE - JSF for UI / fighters.xml - 1
<h:form prependId="false"> <h:panelGrid id="fighterPanel" columns="2"> <h:outputText value="Name"/> <h:inputText id="txt_name" value="#{fighterBean.newFighter.name}"/> <h:outputText value="Lastname"/> <h:inputText id="txt_lastName" value="#{fighterBean.newFighter.lastname}"/> <h:outputText value="Nickname"/> <h:inputText id="txt_nickname" value="#{fighterBean.newFighter.nickname}"/> <h:outputText value="Birth Date (dd-MM-yyyy)"/> <h:inputText id="txt_birthdate" value="#{fighterBean.newFighter.birthDate}"> <f:convertDateTime pattern="dd-MM-yyyy"/> </h:inputText> </h:panelGrid> <h:commandButton id="btn_save" value="Add" action="#{fighterBean.addFighter}"> <f:ajax render="fighters" /> </h:commandButton> </h:form>
Java EE - JSF for UI / fighters.xml - 2 cont’d
<h:dataTable id="fighters" value="#{fighterBean.fighters}" var="f" border="1"> <h:column> <f:facet name="header"> <h:outputText value="Name"/> </f:facet> <h:outputText value="#{f.name}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Last Name"/> </f:facet> <h:outputText value="#{f.lastname}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Nickname"/> </f:facet> <h:outputText value="#{f.nickname}"/> </h:column>
<h:column> <f:facet name="header"> <h:outputText value="Birth Date"/> </f:facet> <h:outputText value="#{f.birthDate}"> <f:convertDateTime pattern="dd-MM-yyyy"/> </h:outputText> </h:column> <h:column> <f:facet name="header"> </f:facet> <h:commandLink action="#{fighterBean.updateFighter(f)}"> <f:ajax render="fighterPanel fighters" /> <h:outputText value="Update" /> </h:commandLink> </h:column> <h:column> <f:facet name="header"> </f:facet> <h:commandLink action="#{fighterBean.removeFighter(f)}"> <f:ajax render="fighterPanel fighters" /> <h:outputText value="Delete" /> </h:commandLink> </h:column> </h:dataTable>
Spring MVC for UI - 1
<mvc:form modelAttribute="fighter" action="saveFighter"> <table> <tr> <td><mvc:label path="name">Name</mvc:label></td> <td><mvc:input path="name" /></td> </tr> <tr> <td><mvc:label path="lastname">Last Name</mvc:label></td> <td><mvc:input path="lastname" /></td> </tr> <tr> <td><mvc:label path="nickname">Nick Name</mvc:label></td> <td><mvc:input path="nickname" /></td> </tr> <tr> <td><mvc:label path="birthDate">Birth Date (dd/MM/yyyy)</mvc:label></td> <td><mvc:input path="birthDate" /></td> </tr> <tr> <td colspan="2"> <input type="submit" value="Submit" /> </td> </tr> </table>
Spring MVC for UI - 2 cont’d
<table style="width: 800px;" border="1"> <tr> <th>Name</th> <th>Last Name</th> <th>Nick Name</th> <th>Birth Date</th> <th/> <th/> </tr> <c:forEach var="f" items="${fighters}"> <tr> <td>${f.name}</td> <td>${f.lastname}</td> <td>${f.nickname}</td> <td>${f.birthDate}</td> <td><a href="<c:url value='editFighter/${f.id}' />">Edit</a></td> <td><a href="<c:url value='removeFighter/${f.id}' />">Delete</a></td> </tr> </c:forEach> </table> </mvc:form>
some more code
@Transactional
• @Transactional under package javax.transaction
• With Java EE7 enables transactions in CDI methods as well as EJB methods.
• isolation levels supported: REQUIRED, REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVER
• EJB3 impl come with JTA configured by default.
• No readOnly transaction support
• @Transactional under package org.springframework.transaction.annotation
• Spring handled transactions with this annotation since v1.2
• Isolation levels supported: Java EE ones + NESTED.
• Transaction manager should be configured in order to handle JTA
@Transactional(readOnly = true)
SpringJavaEE7
AOP and Caching
• ServletFilter
• @WebFilter (Servlet 3.0)
• @Interceptor, @AroundInvoke
• @PostConstruct, @PreDestroy
• Spring provides AOP with proxy pattern at runtime.
• It employs AspectJ annotations for configuration (but not doing compile time weaving…only runtime)
• Spring Caching offers an abstraction on 3rd party cache providers like Ehcache, Hazelcast, Guava and etc.
• Complies with Pointcut designators like, within(), execution(), bean(), @annotation() and others.
SpringJavaEE7
JavaEE - Method Execution Time Logging
@Interceptor public class LogInterceptor { @AroundInvoke public Object doSecurityCheck(InvocationContext context) throws Exception{
long startTime = System.nanoTime(); String className =context.getMethod().getDeclaringClass().getCanonicalName(); String methodName = context.getMethod().getName();
Object execution= context.proceed() long elapsedTime = System.nanoTime() - startTime; Logger.getLogger("LOG").info("Execution of " + className + "#" + methodName + " ended in " + new BigDecimal(elapsedTime).divide(new BigDecimal(1000000)) + " milliseconds"); return execution;
}
@PostConstruct public void onStart(){ Logger.getLogger("SecurityLog").info(“Activating");
} @PreDestroy
public void onShutdown(){ Logger.getLogger("SecurityLog").info(“Deactivating");
} }
Spring - Method Execution Time Logging
@Component@Aspectpublic class ExecutionTimeLogging { @Around("within(com.devchronicles..*)") public Object profile(ProceedingJoinPoint pjp) throws Throwable { long startTime = System.nanoTime(); String className = pjp.getTarget().getClass().getCanonicalName(); String methodName = pjp.getSignature().getName(); Object output = pjp.proceed(); long elapsedTime = System.nanoTime() - startTime; System.out.println("Execution of " + className + "#" + methodName + " ended in " + new BigDecimal(elapsedTime).divide(new BigDecimal(1000000)) + " milliseconds"); return output; }}
Security• Declarative: (basic, form based, digest, client,
mutual)
• via resources: resource/pattern/role
• via annotations: @HttpMethodConstraint, @HttpConstraint, @ServletSecurity
• Programmatic: request.login(userName, password);
• Message Security: via header in SOAP messages
• JAAS & web.xml security
• Alternatively.. Apache Shiro
• Provides w/ subproject Spring Security
• Supports several different authentication methodslogin form–based authentication, authentication with X509 user certificates, LDAP authentication, Windows authentication over legacy NTLM or Kerberos methods, basic and digest authentications.
• Separation of auth. methods from user repositories i.e.: form-based auth from active directory or a DB.
• Supports SSO w/ CAS, OpenID, SiteMinder, and OAuth
• Guest login & built-in remember-me support
• HTTP/HTTPS support
JavaEE7 Spring
Java EE: Basic Authentication with Annotations
@WebServlet(name = "GreetingServlet", urlPatterns = {"/greeting"}) @ServletSecurity( @HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed = {"TutorialUser"}))
Java EE: Form based Authentication with XML<security-constraint>
<display-name>Constraint1</display-name> <web-resource-collection> <web-resource-name>wrcoll</web-resource-name> <description/> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <description/> <role-name>TutorialUser</role-name> </auth-constraint> </security-constraint>
<login-config> <auth-method>FORM</auth-method> <realm-name>file</realm-name> <form-login-config> <form-login-page>/login.xhtml</form-login-page> <form-error-page>/error.xhtml</form-error-page> </form-login-config> </login-config>
<security-role> <description/> <role-name>TutorialUser</role-name> </security-role>
Spring Security Dependencies
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>3.2.6.RELEASE</version> </dependency>
for using Security namespaces.
for securing applications that depends on web services
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>3.2.6.RELEASE</version> </dependency>
• Current latest version of Spring Security is 3.2.6.RELEASE and beware that it ships with spring-core 3.2.8.RELEASE. So better to add 4.1.5.RELEASE dependencies of spring-core manually.
Simplest Spring Security Configuration (Excerpt)
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd" <security:user-service id="userService"> <security:user name="user1" password="secret" authorities="ROLE_USER"/> </security:user-service> <security:authentication-manager> <security:authentication-provider user-service-ref="userService"/> </security:authentication-manager> <security:http pattern="/favicon.ico" security="none"/> <security:http pattern="/app/rest/**" security="none"/> <security:http auto-config="true"> <security:intercept-url pattern="/**" access="ROLE_USER"/> </security:http>
web.xml for handling Spring Security<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/applicationContext.xml</param-value> </context-param> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
Testing• JUnit
• OpenEJB Container (one line) props.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
• Embedded Glasfish Container
• Arquillian container tests
• JUnit + TestNG
• Offers integration testing support w/out the need of deploying app w/ Spring Testing.
• Offers Servlet API mock objects like MockHttpServletRequest, MockHttpServletResponse, MockHttpSession and others.
• Testing REST services is easy with the builder API.
• Bean Profiling allows us to differentiate DB connections for in DEV (derby) or in TEST (hsqldb)
JavaEE7 Spring
Java EE - REST Service Testing
• JUnit
• Mockito
• Simple, just like Java SE
Spring Testing - Maven Dependencies
<spring.version>4.1.5.RELEASE</spring.version> <hamcrest.version>1.3</hamcrest.version> <junit.version>4.12</junit.version> <hsqldb.version>2.3.2</hsqldb.version>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <version>${hamcrest.version}</version> <scope>test</scope> </dependency>
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <exclusions> <exclusion> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>${hsqldb.version}</version> <scope>test</scope> </dependency>
Spring - REST service testing @RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration @ContextConfiguration("classpath*:/applicationContext.xml") public abstract class BaseControllerTests { }
public class FighterRestControllerTests extends BaseControllerTests { private MockMvc mockMvc; @Autowired private WebApplicationContext wac; @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } @Test public void getAllFightersMethodWorksOK() throws Exception { mockMvc.perform(get("/fighters/all")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(content().string(allOf(containsString("{\"id\":1,\"name\":\"Mert\",\"lastname\":\"Caliskan\",\"nickname\":\"mulderbaba\",\"birthDate\":\"1980-12-14\"}")))); }}
Books on Amazon stackoverflow
Release Timeline10.2006
v2 v2.5
11.2007 12.2009 12.2011 12.2012 12.2013 09.2014 06.2015
v3 v3.1 v3.2 v4 v4.1 v4.2
Spring
Java EE
05.1998
JPE J2EE 1.2
12.1999 09.2001 11.2003 5.2006 12.2009 06.2013 Q3 2016
J2EE 1.3 J2EE 1.4 Java EE 5 Java EE 6 Java EE 7 Java EE 8
slowest iteration, biggest change!
Final Verdict
•Spring is cool and so is Java EE.
•They are providing the best of breed (v4 & EE7) same motto, different underlying infra.
•Java EE is standards designator, if you are starting a new project, better to stick w/ the standards.
•If you are into sub-projects of Spring, like -data, -social, -batch, and others like security, leverage the use of Spring.
•We are using both and we’re both happy :)
All sources are available under
•JavaEE7 Version: https://github.com/yenerm/FightClub
•Spring Version: https://github.com/mulderbaba/FightClub
Any questions you have, hmm?
@yenerm@mertcal