effective enterprise java johannes brodwall chief scientist, steria norway
TRANSCRIPT
What I have prepared
In order to design, develop or maintain an enterprise information system in Java (web, database)
As a senior developer I want to learn how to approach important aspects of
my application
My responsibity as an instructor
To give the best answer I can to the problems you have
To be honest when I’m unsure about a question
To listen to your wishes for topics throughout the tutorial
Your responsibilties as participants
To ask your most pressing questionsTo participate activelyTo let me know if we spend too much
time on a topicTo challenge me when you think I’m
wrong
19.04.23
Konfidensiell - Navn på presentasjon.ppt
4
Top take-aways Development
▬ Invest in unit and integration level tests
Architecture▬ Isolate each processing event and abort if you can’t complete
Communication▬ Fowler's First Law of Distributed Object Design: Don’t distribute your
objects Processing
▬ Prefer local transactions to distributed ones State management
▬ Understand the underlying relational model before using an ORM tool Presentation
▬ Design your system from the outside in Security
▬ Treat all data from outside as tainted
Agenda
Architectural overview of enterprise applications Sections (a la carte)
▬ Preparated materials▬ Without prepared materials▬ Suggestions from you▬ Things I don’t know
Breaks at ???
Section menu a la carte
Prepared materials▬ Integration testing▬ Unit testing enterprise applications▬ Persistence (ORM basics, ORM advanced)▬ Scalability▬ Distributed applications▬ Security▬ Client options
Without prepared materials▬ Continuous Deployment▬ Putting transactions, state and processing into context▬ A practical approach to system design▬ Machine-enabled interfaces
Suggestions from you Things I don’t know
Participant stories
In order to <solve a problem in my job> As a <job title> I want to learn about <subject>
Disclaimer!
Code is presented as stand-alone as possible – in real systems, refactor to
remove duplication
Enterprise Java Architecture 2009
Every processing is triggered by an event▬ Usually: Timed trigger, incoming http request, incoming message
All triggers can be forged Every processing event should be isolated
▬ Retrieve all data as you need it▬ Update all data at an end▬ Do optimistic locking▬ All objects are short-lived or immortal and stateless
Unexpected errors should rollback everything, log and abort
Any, oh, EJBs are more trouble than they’re worth
What you need to care about
Your applicationYour application
Interpretrequest
Renderresponse
Identify user
Identifyaction and flow
Read and persistinformation
Isolate processing
What you need to care about
Server moduleServer module
Wire protocol
Identify user
Identifyaction and flow
Read and persistinformation
Server moduleServer module
Present data Manage events
API
Scalability: Only two solutions, one problem
Solution #1: Add more identical nodes Solution #2: Cache at some level
Problem: Data out of sync
Bottlenecks #1: Make one app server performant
1. Poor SQL performance – trace and optimize SQL
2. Garbage collection screw-up – check with a memory profiler
3. Rendering similar pages repeatedly – page caching
4. Stateful pages get in the way of caching – make saner URLs
5. User-info gets in the way of caching – render with JavaScript and cookies
6. Offload static content
7. Increase hardware
Bottleneck #2: Horizontal app server scaling
More seldom than you’re led to think! (e.g. stackoverflow.com)
In principle simple: mod_whatever in Apache frontend Problems: Session Solutions
▬ No session (authentication in perimeter)▬ Sticky sessions▬ Distributed sessions▬ Cookie-based sessions
Bottleneck #3: Database scalability
Two basic solutions▬ Expensive, poorly-understood distributed RDBMSs▬ Caching
Caching:▬ In application server▬ In read-only database mirrors▬ Seperate read and write-requests to different app servers (on
HTTP verbs or URLs)
Tradeoff: How often should users see stale data? Should they ever see own stale data?
Integration testing web applications
@Testpublic void shouldFindSavedPeople() throws Exception { org.mortbay.jetty.Server server = new org.mortbay.jetty.Server(0); server.addHandler(new WebAppContext("src/main/webapp", "/")); server.start();
String rootUrl = "http://localhost:" + server.getConnectors()[0].getLocalPort() + "/";
org.openqa.selenium.WebDriver browser = new org.openqa.selenium.htmlunit.HtmlUnitDriver(); browser.get(rootUrl);
browser.findElement(By.linkText("Create a new person")).click(); browser.findElement(By.name("full_name")).sendKeys("Johannes Brodwall"); browser.findElement(By.name("create")).click();
browser.findElement(By.name("name_query")).sendKeys("johannes"); browser.findElement(By.name("search")).click(); browser.findElement(By.xpath(".//*[text() = 'Johannes Brodwall']"));}
Unit testing servlets with Mockito
@Testpublic void shouldSearchForPerson() throws Exception { HttpServletRequest req = org.mockito.Mockito.mock(HttpServletRequest.class); HttpServletResponse resp = mock(HttpServletResponse.class);
StringWriter pageSource = new StringWriter(); when(resp.getWriter()).thenReturn(new PrintWriter(pageSource));
when(req.getMethod()).thenReturn("GET"); when(req.getPathInfo()).thenReturn("/find.html"); when(req.getParameter("name_query")).thenReturn("foobar");
PersonDao personDao = mock(PersonDao.class); PersonServlet servlet = new PersonServlet(); servlet.setPersonDao(personDao);
servlet.service(req, resp);
verify(personDao).find("foobar"); DocumentHelper.parseText(pageSource.toString()); org.fest.assertions.Assertions.assertThat(pageSource.toString()) .contains("<input type='text' name='name_query' value='foobar'");}
Unit testing Hibernate
@Testpublic void shouldFindPeopleByName() { AnnotationConfiguration configuration = new AnnotationConfiguration(); Properties properties = configuration.getProperties(); properties.setProperty(Environment.URL, "jdbc:h2:mem:person-dao-test;MVCC=true"); properties.setProperty(Environment.DRIVER, Driver.class.getName()); properties.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, ManagedSessionContext.class.getName()); properties.setProperty(Environment.HBM2DDL_AUTO, "update"); configuration.addAnnotatedClass(Person.class); SessionFactory sessionFactory = configuration.buildSessionFactory();
ManagedSessionContext.bind(sessionFactory.openSession());
HibernatePersonDao personDao = new HibernatePersonDao(sessionFactory);
personDao.create(Person.withName("Aye Bee Cee"), Person.withName("Aye Cee")); sessionFactory.getCurrentSession().flush(); sessionFactory.getCurrentSession().clear(); assertThat(personDao.find("Bee")) .contains(Person.withName("Aye Bee Cee")) .excludes(Person.withName("Aye Cee"));
ManagedSessionContext.unbind(sessionFactory).close();}
Why?
In order to reduce the amount of dedious work and error prone SQL/jdbc code I write
As a developer who already understands SQL well, I want to use an ORM to manage my persistence
Basics
@Entitypublic class Person {
@Id @GeneratedValue(strategy=GenerationType.SEQUENCE) private Long id;
private String name;
@Override public boolean equals(Object obj) { }
@Override public int hashCode() { }
}
Advanced: Hacked polymorphic joins
@Entity @Table(name="XX_KONTO")public class Konto implements Serializable {
@EmbeddedId private KontoPk id = new KontoPk();
@Column(name="TREKKPST") private Integer trekkprosent;
@ManyToOne(optional=true) @NotFound(action=NotFoundAction.IGNORE) @JoinColumn(name="fodselsnr", referencedColumnName="fodselsnr") private Person person;
@ManyToOne(optional=true) @NotFound(action=NotFoundAction.IGNORE) @JoinColumn(name="orgnr", referencedColumnName="orgnr")
private Foretak foretak;
public KontoEier getEier() { return person != null ? person : foretak; }
Advanced: Composite foreign keys as part of primary key
@ManyToOne @JoinColumns({ @JoinColumn(name="fylkenr", referencedColumnName="fylkenr", insertable=false, updatable=false), @JoinColumn(name="komnr2", referencedColumnName="komnr2", insertable=false, updatable=false), @JoinColumn(name="ktotype", referencedColumnName="ktotype", insertable=false, updatable=false), @JoinColumn(name="ktonr", referencedColumnName="ktonr", insertable=false, updatable=false)}) private Konto konto;
Advanced: Composite foreign keys as part of primary key
@ManyToOne @JoinColumns({ @JoinColumn(name="fylkenr", referencedColumnName="fylkenr", insertable=false, updatable=false), @JoinColumn(name="komnr2", referencedColumnName="komnr2", insertable=false, updatable=false), @JoinColumn(name="ktotype", referencedColumnName="ktotype", insertable=false, updatable=false), @JoinColumn(name="ktonr", referencedColumnName="ktonr", insertable=false, updatable=false)}) private Konto konto;
Advanced: Fetch strategies
public enum FetchType {/** * Defines that data must be lazily fetched */LAZY,/** * Defines that data must be eagerly fetched */EAGER};
Always understand what the ORM does
Fetching strategies:▬ Left outer join▬ Lazy loading
Inheritance▬ Table-per-concrete class (union subclass)▬ Table-per-class (join subclass)▬ Table-per-class-hierarchy ()
Relationships▬ @OneToMany @ManyToOne▬ Cascading (broken)▬ Ownership, mapped-by
When in doubt – make sure to unit test!
Distributed applications
Fowler’s first law of distributed objects:▬ Don’t distribute your objects
As the server:▬ Treat your clients as you would a human client: Designed
interface, test at the outside, have a ”designer” take a look
As the client:▬ Each service you use will decrease your understanding,
reliability and performance
Why?
In order to get access to data or a service owned by another entity As a complex application I want to integrate with the third party service
In order to profit on a valuable service or dataset that I control As a service provider I want to provide a remote API to my services
In order to create a rich client interface As a the client side of a rich internet application I want to communicate remotely with the server
Bad reasons!
In order to reuse functionality As a complex application I want to call a third party internal service (Seldom worth the saving)
In order to don’t communicate with some other developer
As an introvert loner I want to hide myself behind a service interface (How will you be able to assess total complexity?)
How
REST vs SOAP vs ORB▬ REST is good when you want to use HTTP as an application
protocol and not just a transport protocol▬ ORBs are good when you control both endpoints▬ SOAP is good when ORBs would be better▬ Hessian is a binary ORB-protocol over HTTP
Distributed transactions versus idempotent writes▬ Very few organizations have been able to master distributed
transactions▬ Understand the semantics of HTTP PUT
Security thread #1: Injection
Problem: Client tricks the application to treat data as instructions
▬ SQL commands▬ HTML code▬ (In C: return pointer in memory)
Solution: Treat all outside data as tainted▬ Outside: Request (obviously), files (usually), database (yes!)
Always, alway, always escape tainted data▬ SQL parameters – Use PreparedStatement▬ Hibernate parameters – Use parameters or Criteria▬ HTML snippet – escape
Security threat #2: Client-only validation
Attack: A malicious user looks at a form and changes the parameters en-route (Firefox plugin)
Weakness: The server expects incoming data to be valid
Solution (again): Treat all incoming data as tainted
Security threat #3: Request forgery
Problem: A malicious site tricks the user into submitting a rogue post-request to you
Problem: A malicious email tricks the user into clicking a rogue link to your application
Solution (again): Treat all incoming data as tainted
Things to consider
Power of expression▬ Flex, JavaFX, GWT
Testing round-trip▬ HTML, Swing, (JavaFX?)
Easy to layout good Uis?▬ Flex, JavaFX(?)
Simplicity of language▬ Swing, GWT
Client pervasiveness/perceived ease of installation▬ HTML, GWT, (Flex)
Client performance requirements▬ HTML, (GWT)
Things that aren’t issues any more
Communication between client and server▬ Basically, all use http-based async ORBs▬ With Swing: Make your own async http ORB a la GWT with
SwingWorker and Hessian
Distribution of client▬ Swing and JavaFX use JavaWebStart
Retrospective
What new thing have I learned? Which problems at work will I apply this to? What is my most urgent question remaining?
Johannes BrodwallChief ScientistSteria, Norway
[email protected]://johannesbrodwall.com@jhannes
Thank you for participating
Chapter 2. Architecture
Item 1: Prefer components as the key element of development, deployment, and reuse Item 2: Prefer loose coupling across component boundaries Item 3: Differentiate layers from tiers Item 4: Keep data and processors close together Item 5: Remember that identity breeds contention Item 6: Use hook points to inject optimizations, customizations, or new functionality Item 7: Be robust in the face of failure Item 8: Define your performance and scalability goals Item 9: Restrict EJB to transactional processing Item 10: Never optimize without profiling first Item 11: Recognize the cost of vendor neutrality Item 12: Build in monitoring support Item 13: Build in administration support Item 14: Make deployment as simple as possible
Chapter 3. Communication
Item 15: Understand all your communications options Item 16: Consider your lookup carefully Item 17: Recognize the cost of network access Item 18: Prefer context-complete communication styles Item 19: Prefer data-driven communication over behavior-driven
communication Item 20: Avoid waiting for remote service requests to respond Item 21: Consider partitioning components to avoid excessive load on
any one machine Item 22: Consider using Web Services for open integration Item 23: Pass data in bulk Item 24: Consider rolling your own communication proxies
Chapter 4. Processing
Item 25: Keep it simple Item 26: Prefer rules engines for complex state evaluation and execution Item 27: Prefer transactional processing for implicitly nonatomic failure scenarios Item 28: Differentiate user transactions from system transactions Item 29: Minimize lock windows Item 30: Never cede control outside your component while holding locks Item 31: Understand EJB transactional affinity Item 32: Prefer local transactions to distributed ones Item 33: Consider using optimistic concurrency for better scalability Item 34: Consider using pessimistic concurrency for explicit concurrency control Item 35: Consider lower isolation levels for better transactional throughput Item 36: Use savepoints to keep partial work in the face of rollback Item 37: Replicate resources when possible to avoid lock regions Item 38: Favor the immutable, for it needs no locks
Chapter 5. State Management
Item 39: Use HttpSession sparingly Item 40: Use objects-first persistence to preserve your domain model Item 41: Use relational-first persistence to expose the power of the
relational model Item 42: Use procedural-first persistence to create an encapsulation layer Item 43: Recognize the object-hierarchical impedance mismatch Item 44: Use in-process or local storage to avoid the network Item 45: Never assume you own the data or the database Item 46: Lazy-load infrequently used data Item 47: Eager-load frequently used data Item 48: Batch SQL work to avoid round-trips Item 49: Know your JDBC provider Item 50: Tune your SQL
Chapter 6. Presentation
Item 51: Consider rich-client UI technologies Item 52: Keep HTML minimal Item 53: Separate presentation from processing Item 54: Keep style separate from content Item 55: Pregenerate content to minimize processing Item 56: Validate early, validate everything
Chapter 7. Security
Item 57: Security is a process, not a product Item 58: Remember that security is not just prevention Item 59: Establish a threat model Item 60: Assume insecurity Item 61: Always validate user input Item 62: Turn on platform security Item 63: Use role-based authorization Item 64: Use SignedObject to provide integrity of Serialized
objects Item 65: Use SealedObject to provide confidentiality of
Serializable objects Item 66: Use GuardedObject to provide access control on objects
Chapter 8. System
Item 67: Aggressively release resources Item 68: Tune the JVM Item 69: Use independent JREs for side-by-side versioning Item 70: Recognize ClassLoader boundaries Item 71: Understand Java Object Serialization Item 72: Don't fight the garbage collector Item 73: Prefer container-managed resource management Item 74: Use reference objects to augment garbage collection
behavior Item 75: Don't be afraid of JNI code on the server