Farewell Old Application Server The Server Container Anti- Pattern and how to avoid it.

159
Farewell Old Application Server The Server Container Anti- Pattern and how to avoid it

Transcript of Farewell Old Application Server The Server Container Anti- Pattern and how to avoid it.

Farewell Old Application Server

The Server Container Anti-Pattern and how to avoid it

(Shameless plug)

.meetup.com/13

Why am I standing here?

I believe

that you, as a developer,

should care about deployment.

Two things you will learn:

1. How to create a non-server

2. Practical Configuration Management

My story:

By company decree

we were using a traditional application server.

(WebSphere)

Unhappily.

It was ”somebody else’s problem.”

We couldn’t do what we wanted.

(Continuous deployment!)

Complexity was killing our projects.

We started caring.

We decided to cut the Gordian knot.

Our solution:

• Standard Java

• Maven

• Jetty

Let’s see how it works.

Can’t change parent!

A developer has released a new version (1.0.1).

Create a test environment and try it out.

Server: Initial installation

./install.sh <env> 1.0.1

Server: Upgrade production

Lesson learned: Package everything you need.

Experience: Separate information that should

survive upgrades.

Symbolic link

port=9090shutdown_port=9190

datasource.url=jdbc:mysql://localhost/nocontainer_devdatasource.username=johannesdatasource.password=johannes

New requirement: Delete

Testing: Embedded integration test

public void testDeleteCategory() throws Exception { // Insert test data Category category = new Category(uniqueName()); Serializable key = getRepo().insert(category); getRepo().writeChanges();

// Ensure that data is displayed in index beginAt("categories/list.html"); assertTextPresent("Showing all categories"); assertLinkPresentWithText(category.getName());

// Delete it beginAt("categories/edit.html?id=" + key); submit("delete");

// Ensure that after delete, we go to index assertTextPresent("Showing all categories");

// Ensure that the category is no longer there assertLinkNotPresentWithText(category.getName());}

public void testDeleteCategory() throws Exception { // Insert test data Category category = new Category(uniqueName()); Serializable key = getRepo().insert(category); getRepo().writeChanges();

// Ensure that data is displayed in index beginAt("categories/list.html"); assertTextPresent("Showing all categories"); assertLinkPresentWithText(category.getName());

// Delete it

// Ensure that the category is no longer there assertLinkNotPresentWithText(category.getName());}

public void testDeleteCategory() throws Exception { // Insert test data

// Ensure that data is displayed in index

// Delete it beginAt("categories/edit.html?id=" + key); submit("delete");

// Ensure that after delete, we go to index assertTextPresent("Showing all categories");

// Ensure that the category is no longer there}

public void testDeleteCategory() throws Exception { // Insert test data

// Ensure that data is displayed in index

// Delete it beginAt("categories/edit.html?id=" + key); submit("delete");

// Ensure that after delete, we go to index assertTextPresent("Showing all categories");

// Ensure that the category is no longer there}

<html>

<body> <h2 id="name">${category.name}</h2>

#showErrors("category")

<form method="POST"> <p><strong>Parent:</strong> #selectForObject("category.parent" $allCategories)</p> <p>Description: #springFormInput("category.description" "") #springShowErrors("<br>" "")</p> <p>Type: #selectForEnum("category.type" $categoryTypes)</p>

<input type="submit" value="Submit" /> </form>

<p><a href="list.html">List all</a></p>

</body></html>

<html>

<body> <h2 id="name">${category.name}</h2>

#showErrors("category")

<form method="POST"> <p><strong>Parent:</strong> #selectForObject("category.parent" $allCategories)</p> <p>Description: #springFormInput("category.description" "") #springShowErrors("<br>" "")</p> <p>Type: #selectForEnum("category.type" $categoryTypes)</p>

<input type="submit" value="Submit" /> </form>

<form method="POST"> <input type="submit" name="delete" value="Delete" /> </form>

<p><a href="list.html">List all</a></p>

</body></html>

public void testDeleteCategory() throws Exception { // Insert test data

// Ensure that data is displayed in index

// Delete it beginAt("categories/edit.html?id=" + key); submit("delete");

// Ensure that after delete, we go to index assertTextPresent("Showing all categories");

// Ensure that the category is no longer there}

Debugging: Just like a main method

protected ModelAndView onSubmit( HttpServletRequest req, HttpServletResponse resp, Object command, BindException errors) { Category category = (Category)command; if (category.getId() == null) { Serializable key = repository.insert(category); return new ModelAndView(new RedirectView("show.html?id=" + key)); }

repository.update(category); // Redirect on post - go back to the same page return new ModelAndView(

new RedirectView(req.getRequestURI() + "?" + req.getQueryString()));}

protected ModelAndView onSubmit( HttpServletRequest req, HttpServletResponse resp, Object command, BindException errors) { Category category = (Category)command; if (category.getId() == null) { Serializable key = repository.insert(category); return new ModelAndView(new RedirectView("show.html?id=" + key)); } if (req.getParameter("delete") != null) { repository.delete(Category.class, category.getId()); return new ModelAndView(new RedirectView("list.html")); } repository.update(category); // Redirect on post - go back to the same page return new ModelAndView(

new RedirectView(req.getRequestURI() + "?" + req.getQueryString()));}

Continuous integration

(Not demoed)

mvn deploy

The Simplest Thing...

... cron

Mon Sep 10 18:37:02 EDT 2007: Not upgraded, skipping restartMon Sep 10 18:38:13 EDT 2007: Installed new versionStoppingStartingTesting connection (user=johannes, url=jdbc:mysql://localhost/nocontainer_dev)...OK!Deploy 'nocontainer' from /home/nocontainer/test/nocontainer-app/curr/repo/com/brodwall/nocontainer/nocontainer-web/1.1-SNAPSHOT/nocontainer-web-1.1-SNAPSHOT.warServer started on http://localhost:9090 in 12.547sMon Sep 10 18:39:02 EDT 2007: Not upgraded, skipping restart

Experience: Look like a UNIX service

./prod/nocontainer-app/operate.sh start

./prod/nocontainer-app/operate.sh stop

./prod/nocontainer-app/operate.sh status

Release: mvn release

Summary

1. Add a test

2. Make it pass

3. Check it in

4. Continuous integration and deployment

5. Run it at test server

6. Deploy to staging

6. Final verifications

7. Deploy to production

Results:

1. Better management

2. Simplicity

3. Isolation

4. Better development support

1: Single commands to...

... Build

... Install

... Upgrade

=> Ease of deployment

2: A simpler environment

... No dependencies between servers

... We own the Main class

Scripted server acts as UNIX service.

Upgrade with ssh, cron

Avoid EJBs, JMS, XA

3: Better isolation between applications

(especially: JVM version)

The ultimate irony:

JPA (EJB 3) via Hibernate on Java SE > 1.5

Our WebSphere servers still don’t support 1.5

Avoid Java EE server to get Java EE features

4: Better development attributes

Embedded integration tests

Easier to debug on server

Exact same software in debugger and server

Same versions of all libraries

Free, low footprint installation on workstation

Challenges:

How ready is Jetty for production?

What is being run in the wild?

Jetty: 221k

(Okay, then)

Load balancing and failover

(No state in application server)

Two week intensive stress testing

We could not make it fail

Organization: Open source procurement process

Organization: Showing this to the WebSphere guys

Support: Per project

Application management shortcomings

”On what servers is this application running?”

”What applications are running on this server”

Making: ”jettyctl”

Requires some documentation

Don’t go overboard with management

YAGNI

Why Anti-pattern

Insufficent isolation means risk

Insufficient isolation means older versions

Outside-in means lack of control

Outside-in means lack of understanding

Outside-in means complexity

Deployment apps means manual work

Conclusion:

In Soviet Russia, Application Server contains you!

We have turned the app server inside out

We choose Jetty, Maven, mod_proxy

These worked for us

(You may prefer glassfish, webstone or whatever)

(Shameless plug)

Q&A