javafx and scala in the cloud
DESCRIPTION
Talk about an end-to-end application leveraging JavaFX, Scala, Java DB, and cloud technologies. Given at the JAX conference in San Francisco.TRANSCRIPT
Stephen Chin | OracleAndrew Phillips | jclouds
JavaFX and Scala in the Cloud
@steveonjava
@jclouds
+
Heaven
Photo by Alberto Fernandez Fernandez
When This Architecture Makes Sense
• Data is mostly read-only– Transactional updates still require a server (but
can be simpler/smaller)
• User's view of data is small to medium– Initial DB download of < 10K records is reasonable– Not total DB size, but subset of data visible to user
Conference App has 3K large records and compresses to only 330KB DB size
Cloud Data Advantages
• Offline Operation– Once DB is cached, application works 100% offline
• Responsive Client Performance– All DB queries are fast and local
• High Availability & Scalability– 99.99% Availability– Easily scales up to 100s of requests per second
But, with proper hashes scales up to millions of requests per second!
Cloud Data Advantages (continued)
• Insanely cheap server costs!
Number of Users Monthly Cost*
3,000 Free (S3 free tier)
10,000 $0.28
100,000 $3.84
1,000,000 $39.48
* For 330KB of hosted data in Amazon S3
Cloud Data Advantages (continued)
• Insanely cheap server costs!
Number of Users Monthly Cost*
3,000 Free (S3 free tier)
10,000 $0.28
100,000 $3.84
1,000,000 $39.48
* For 330KB of hosted data in Amazon S3
Cloud Data Advantages (continued)
• Insanely cheap server costs!
Number of Users Monthly Cost*
3,000 Free (S3 free tier)
10,000 $0.28
100,000 $3.84
1,000,000 $39.48
* For 330KB of hosted data in Amazon S3
Cloud Data Advantages (continued)
• Insanely cheap server costs!
Number of Users Monthly Cost*
3,000 Free (S3 free tier)
10,000 $0.28
100,000 $3.84
1,000,000 $39.48
* For 330KB of hosted data in Amazon S3
UI
JavaFX 2.0 PlatformImmersive Application Experience
Leverage your Java skills with modern JavaFX APIs
• Cross-platform Animation, Video, Charting
• Integrate Java, JavaScript, and HTML5 in the same application
• New graphics stack takes advantage of hardware acceleration for 2D and 3D applications
• Use your favorite IDE: NetBeans, Eclipse, IntelliJ, etc.
What is Scala
• Started in 2001 by Martin Odersky• Compiles to Java bytecodes• Pure object-oriented language• Also a functional programming language
2001• Scala Started
2003/2004• Scala v1.0
2006• Scala v2.0
2011• Scala 2.9.2 (latest)
Why Scala?
• Shares many language features with JavaFX Script that make GUI programming easier:– Static Type Checking – Catch your errors
at compile time– Closures – Wrap behavior and pass it by
reference– Declarative – Express the UI by describing
what it should look like
Why Scala?
• Scala also supports Type Safe DSLs!– Implicit Conversions – type safe class
extension– Operator Overloading – with standard
precedence rules– DelayedInit / @specialized – advanced
language features
(continued)
Java vs. Scala DSLpublic class JavaFXEEDemo extends Application {
public static void main(String[] args) { launch(JavaFXEEDemo.class, args); } private SpeakerModel speakerModel = getInstance(); private TextField filter; private ChoiceBox<String> items; @Override public void start(Stage primaryStage) { primaryStage.setTitle("JavaOne Speaker List"); speakerModel.load(); EventHandler<ActionEvent> filterAction = new EventHandler<ActionEvent>() { public void handle(ActionEvent event) { String field = items.selectionModelProperty().getValue().getSelectedItem(); String text = filter.getText(); speakerModel.filter(field, text); } }; primaryStage.setScene(SceneBuilder.create() .width(625) .height(700) .fill(Color.web("#fcfcfc")) .root(StackPaneBuilder.create().children( // Background image and gradient VBoxBuilder.create().children( ImageViewBuilder.create() .image(new Image(getClass().getResourceAsStream("JavaOneLogo.png"))).build(), RectangleBuilder.create().width(625).height(50).fill(LinearGradientBuilder.create().endX(0).stops( StopBuilder.create().color(Color.WHITE).offset(0).build(), StopBuilder.create().color(Color.web("#d0cbc8")).offset(1).build() ).build()).build() ).build(), // Foreground controls VBoxBuilder.create() .padding(new Insets(100, 20, 20, 20)) .spacing(30) .children(HBoxBuilder.create() .alignment(Pos.BASELINE_LEFT) .spacing(15) .children( items = new ChoiceBox<String>( FXCollections.observableArrayList(FIRST_NAME, LAST_NAME, JOB_TITLE, COMPANY) ), filter = TextFieldBuilder.create().prefColumnCount(20).onAction(filterAction).build(), ButtonBuilder.create().text("Filter").onAction(filterAction).build(), ButtonBuilder.create().text("Clear").onAction(new EventHandler<ActionEvent>() { public void handle(ActionEvent event) { speakerModel.clearFilter(); } }).build(), ButtonBuilder.create().text("Reload").onAction(new EventHandler<ActionEvent>() { public void handle(ActionEvent event) { speakerModel.load(); } }).build() ).build(), TableViewBuilder.<Speaker>create().items(speakerModel.getFilteredData()).prefHeight(1000).columns( TableColumnBuilder.<Speaker, String>create() .text(FIRST_NAME) .cellValueFactory(new PropertyValueFactory<Speaker, String>(FIRST_NAME_FIELD)).build(), TableColumnBuilder.<Speaker, String>create() .text(LAST_NAME) .cellValueFactory(new PropertyValueFactory<Speaker, String>(LAST_NAME_FIELD)).build(), TableColumnBuilder.<Speaker, String>create() .text(JOB_TITLE) .prefWidth(200) .cellValueFactory(new PropertyValueFactory<Speaker, String>(JOB_TITLE_FIELD)).build(), TableColumnBuilder.<Speaker, String>create() .text(COMPANY) .prefWidth(212) .cellValueFactory(new PropertyValueFactory<Speaker, String>(COMPANY_FIELD)).build() ).build() ).build() ).build() ).build() ); items.getSelectionModel().selectFirst(); primaryStage.show(); }}
object ConferenceUI extends JFXApp { val model = ConferenceModel stage = new Stage { width = 625 height = 700 scene = new Scene(new StackPane()) { fill = "#fcfcfc" children = Seq( new VBox { children = Seq( new ImageView { image = new Image(getClass().getResourceAsStream("JavaOneLogo.png")) }, new Rectangle { width = 625 height = 50 fill = new LinearGradient( endX = 0, stops = Stops(WHITE, "#d0cbc8") ) } ) }, new VBox { padding = Insets(100, 20, 20, 20) spacing = 30 children = Seq( new HBox { val filter = new TextField(); val items = new ChoiceBox[ruco.TextField[Speaker]]() { items = ObservableBuffer(Speaker.firstName, Speaker.lastName, Speaker.jobTitle, Speaker.company) converter = StringConverter.toStringConverter({s:ruco.TextField[Speaker] => s.name}) } alignment = Pos.BASELINE_LEFT spacing = 15 children = Seq( items, filter, new Button("Filter") { onAction = { e:ActionEvent => model.filter(items.selectionModel().getSelectedItem(), filter.text()) } }, new Button("Clear") { onAction = { e:ActionEvent => filter.text = "" model.clear() } }, new Button("Reload") { onAction = { e:ActionEvent => filter.text = "" model.load() } } ) items.selectionModel().selectFirst() }, new TableView[Speaker](model.filteredSpeakers) { columns = Seq( new TableColumn[Speaker, String] { text = "First Name" converter = {_.firstName()} }, new TableColumn[Speaker, String] { text = "Last Name" converter = {_.lastName()} }, new TableColumn[Speaker, String] { text = "Job Title" converter = {_.jobTitle()} prefWidth = 200 }, new TableColumn[Speaker, String] { text = "Company" converter = {_.company()} prefWidth = 212 } ) prefHeight = 1000 } ) } ) } onCloseRequest = {_:Any => Platform.exit} }}
83 Lines2622 Characters
88 Lines1452 Characters
ScalaFX Application
object ConferenceUI extends JFXApp { val model = ConferenceModel stage = new Stage { width = 625 height = 700 scene = new Scene(new StackPane()) { fill = "#fcfcfc" children = Seq( // create background // create foreground ) } }}
Loading Images
new ImageView { image = new Image( getClass().getResourceAsStream( "JavaOneLogo.png" ) )}
Creating Buttons
new Button("Filter") { onAction = { e:ActionEvent => model.filter(items.selectionModel().getSelectedItem(), filter.text()) }}
new Button("Clear") { onAction = { e:ActionEvent => filter.text = "" model.clear() }}
ScalaFX Table Construction
new TableView[Speaker](model.filteredSpeakers) { columns = Seq( new TableColumn[Speaker, String] { text = "First Name" converter = {_.firstName()} }, new TableColumn[Speaker, String] { text = "Last Name" converter = {_.lastName()} } … ) prefHeight = 1000}
DATABASEBy RRZEicons (Own work) [CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons
Java DB / Apache Derby
• Embedded Database• Small Footprint (2.7MB)• Standards Based (Java, JDBC, SQL)• Extremely Easy to Configure
– With JDBC 4 / SE 6, just drop in the jar!
Circumflex ORM
• Scala-based ORM(Object-RelationalMapping)
• SQL-based Query Syntax• DSL for Domain Object Definition• Lazy and Eager Fetch Strategies
Embedded DB Config
orm.connection.driver= org.apache.derby.jdbc.EmbeddedDriverorm.connection.url=jdbc:derby:conferenceDataorm.connection.username=user1orm.connection.password=user1orm.defaultSchema=APP
Speaker Domain Object
class Speaker extends Record[String, Speaker] { val id = "id".VARCHAR(255).NOT_NULL val company = "company".VARCHAR(255) val firstName = "firstName".VARCHAR(255) val jobTitle = "jobTitle".VARCHAR(255) val lastName = "lastName".VARCHAR(255)
def PRIMARY_KEY = id def relation = Speaker}object Speaker extends Speaker with Table[String, Speaker]
Query the Database
def clear() { val speakers = Speaker.criteria.list() filteredSpeakers.setAll(speakers)}
def filter(field: TextField[Speaker], filterString: String) { val speakers = Speaker.criteria.add( field LIKE "%" + filterString + "%").list() filteredSpeakers.setAll(speakers)}
CLOUD
@jclouds
An OSSM Persistence Store
• On-demand• Self-service• Scalable• Measurable
• ™ Dave Nielsen, CloudCamp
open source
simple: feels like java (and clojure)unit testable
tested across multiple clouds
vibrant community
BlobStore LoadBalancer
Compute What do you want?
Portable APIs
Embeddable
Provider-Specific Hooks
github jclouds-examples
@jclouds
Anatomy of a BlobStore Project
1.Create context
2.Get BlobStore API
3.Do stuff
4.Close context
jclouds modularity
APIs are software focused Providers are offering focused
API + location + defaults = Provider
Cloud Access in Scala
val context = ContextBuilder.newBuilder("aws-s3") .credentials("identity", "secret") .buildView(classOf[BlobStoreContext])
def loadFromCloud(container:String, resource:String):InputStream = { val blobStore = context.getBlobStore val blob = blobStore.getBlob(container, resource) blob.getPayload.getInput}
def close() { context.close()}
@jclouds
Why jclouds?• Data Portability
o APIs are not as compatible as they might appear• Code Portability
o Currently 33 cloud providers• Enterprise-grade
o Move petabytes of data• Parallel operations without threading
concernso Outperforms many native SDKso GAE compatibleo Many tuning options
@jclouds
Why jclouds?
• OSGi compatible• Clojure binding• “Invented” many standard SDK features
o e.g. sync/async APIs
• Tested!o “official” TCK for a number of cloud providerso also supports offline/local testing
@jclouds
Why jclouds?
• Location metadatao Don’t get locked in to a provider’s deployment policy
• Does the hard work so you don’t have too Multi-part in native SDKs vs. .multipart() in
jclouds
• Strong & active communityo ~65 contributors, commercial support
Conference App Demo
Stephen Chin <[email protected]> | Oracle @steveonjava
Andrew Phillips <[email protected]> | jclouds @jclouds
Thank You!