Busy Java Developer's Workshop using Apche Isis Ted Neward Neward - - PowerPoint PPT Presentation
Busy Java Developer's Workshop using Apche Isis Ted Neward Neward - - PowerPoint PPT Presentation
Busy Java Developer's Workshop using Apche Isis Ted Neward Neward & Associates http://www.tedneward.com | ted@tedneward.com Objectives Why are we here? Explore concepts around "domain objects" specifically, the idea of
Objectives
Why are we here?
– Explore concepts around "domain objects"
specifically, the idea of using them to build an application
– Understand the history of Smalltalk and MVC – Play around with NakedObjects
- Apache Isis (Java)
- RestfulObjects (HTTP-API)
Credentials
Who is this guy?
– Principal -- Neward & Associates – Director -- Developer Relations, Smartsheet.com – Microsoft MVP (F#, C#, Architect); Java Expert (JSRs 175, 277) – Author
Professional F# 2.0 (w/Erickson, et al; Wrox, 2010) Effective Enterprise Java (Addison-Wesley, 2004) SSCLI Essentials (w/Stutz, et al; OReilly, 2003) Server-Based Java Programming (Manning, 2000)
– Blog: http://blogs.tedneward.com – Writing: http://www.newardassociates.com/#/writing – Twitter: @tedneward – For more, see http://www.newardassociates.com/#/about
Format
How we gonna do this?
Format
Lecture/lab format
– I will lecture for a bit – You will code/debug/explore for a bit
I will try to wander the room and help where I can
– Lather, rinse, repeat – Turn in your evals and you are free!
Format
"The Rules"
– Pairing and interaction
absolutely encouraged
– Googling/Binging/looking stuff up online
absolutely encouraged
– Leaving the room during a "lab"
absolutely encouraged (if necessary)
– Jumping ahead
encouraged, but I can only help for "this" step
Format
"Requests"
– set the phone/volume/etc to vibrate
basically, don't disturb the neighbors
– ask questions!
if you're thinking it, so are other people
– ... but let's keep the focus to the here and now
there's a lot to cover, so I need to keep us focused
Format
Ready?
– Any questions? – If not, here we go.... – Last chance... abandon hope, all ye who remain...
History
Where it all began
Smalltalk: Overview
The original O-O language
Overview
Smalltalk is...
– object-oriented – dynamically-typed – message-passing – reflective
... programming language ... and environment
Overview
Current Smalltalk implementations
– Amber (runs in Javascript browsers!)
http://www.amber-lang.net/
– Squeak
https://en.wikipedia.org/wiki/Squeak
– GNU Smalltalk
https://en.wikipedia.org/wiki/GNU_Smalltalk
– Concomm Smalltalk (ObjectStudio, VisualWorks)
http://www.cincomsmalltalk.com/main/
– Pharo
http://pharo.org/
Smalltalk
A History
History
Smalltalk's history is long and distinguished
– began development in 1969 – developed at Xerox PARC – invented by Alan Kay (with help from Dan Ingalls) – influenced by Lisp, Simula, Logo, Sketchpad – first release -- Smalltalk-71 – first public release -- Smalltalk-80
History
Smalltalk influenced...
– AppleScript – Common Lisp Object System – Dart – Dylan – Erlang – Etoys – Falcon
History
Smalltalk influenced...
– Go – Groovy – Io – Ioke – Java – Lasso – Lisaac
History
Smalltalk influenced...
– Logtalk – NewtonScript – Object REXX – Objective-C – PHP 5 – Perl 6
History
Smalltalk influenced...
– Python – Ruby – Scala – Scratch – Self
History
Began on a bet
– can a programming language based on the idea of message passing inspired by Simula be implemented in 'a page of code'? – incidentally, Kay answered that in "a few mornings"
History
Objects introduced in Smalltalk-80
– Smalltalk object can do three things:
- hold state (references to other objects)
- receive a message from itself or another object
- in the course of processing a message, send a message to
itself or another object
– no difference between values which are objects and primitive types – "in Smalltalk everything is an object" – classes are also instances of objects
History
Smalltalk incorporated IDE and runtime into one tool
– this is the "Smalltalk browser" – objects were visual objects – manipulate the objects to manipulate the environment – image files were of the entire runtime
Model/View/Controller
Separating display from logic
Model/View/Controller
MVC is a design pattern that appears frequently in GUI systems
– Model represents the state – Controller represents the logic – View represents the display/UI
Model/View/Controller
Origins lie in Smalltalk
– Models were objects – Controllers were objects – Views were objects – Today, this is an idiom known as "Naked Objects"
Model/View/Controller
MVC showed up in a number of GUI systems
– Windows UI frameworks called it "Document/View" – Java Swing built around MVC quite deeply
- tables, lists, all used models extensively
- even the actual UI code was split along view/controller lines;
AbstractButton defined basic button behavior, and deferred to another class to do the actual pixel-drawing work
– iOS uses it (ViewControllers are controllers)
Model/View/Controller
In the original MVC...
– Models were often connected to more than one View – Controllers coordinated view updates as model changed – Models, Views and Controllers relatively loosely coupled – Web adaptation of MVC changed this to a 1:1 View/Controller
Web views and controllers never reused
Model/View/Controller
Parting thoughts
– Models are not necessarily Domain Objects
community is split over this one
– Views should not contain "logic"
- except for logic that is UI-focused
- community is split over this one too
Model/View/Controller
MVC has spawned a lot of similar ideas/patterns
– Hierarchical model-view-controller – Model-view-adapter – Model-view-presenter – Model View ViewModel – Observer – Presentation-abstraction-control – Three- or n-tier architecture
Model/View/Controller
Resources
– For more details, see
http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80 %93controller
– Observer pattern comes from "Design Patterns" (GOF book)
Gamma, Helm, Johnson, Vlissides
– Presentation-abstraction-control comes from "POSA 1"
Buschmann, Meuneir, Rohnert, Sommerlad, Stal
Naked Objects
Wait.... what?
Naked Objects: Overview
In the old days...
– objects were intended to be directly user-manipulable things
- data directly modified/changed
- methods directly invoked
- connections directly displayed/modified
– ... but we have since lost that simple idea
Naked Objects: Overview
What's wrong with the way we do applications today?
– too much complexity in the UI code – too many opportunities to get the business rules wrong – too much time spent building the system – too much effort required to understand/maintain it
Naked Objects: Overview
New idea: Let the UI center around the objects
– "All business logic should be encapsulated onto the domain objects." – "The user interface should be a direct representation of the domain objects, with all user actions explicitly consist in the creating or the retrieving of domain objects and/or invoking methods on those objects." – "The user interface shall be 100% automatically created from the definition of the domain objects."
Naked Objects: Overview
Benefits:
– faster development cycle – greater agility – more user-centric/empowered user interface – easier requirements analysis
Naked Objects: Basics
Understand the principles of Naked Objects
Basics
Naked Objects frameworks core concepts
– Domain Objects
- properties
- collections
- actions
– Value types – Non-objects
- Factories/Repositories
- services
- external/system services
– ViewModels
Basics
Domain objects
– domain objects represent the persistent entities described within the system – these are your "plain old objects" (POJOs, POCOs, etc) – typically they intend to represent state plus behavior
- state is expressed as properties
- behavior is expressed as actions
Basics
Properties
– represents an element of state in a domain object – typically one of three types
- value property (property of value type)
- reference property (association to another domain object)
- collection property (collection of properties)
– properties can have some UI logic or behavior attached to them
- control the order in which properties are displayed
- trigger behavior when a property is changed
- input validation
Basics
Actions
– actions are methods intended for user invocation – typically these are "just methods" that are discovered
Basics
Value types
– these are basically non-referenced objects – we care only for their value – they lack a standalone "identity" – embedded "in-place" for storage
Basics
Services
– services are code that aren't attached specifically to
- bjects
these are often pure logic/behavior/transformational in nature
– NakedObject services come in three flavors:
- creating/retrieving domain objects (factories/repositories)
- provide a bridge to external functionality (external services)
- provide functionality shared by multiple domain objects
which do not share a common superclass (contributed actions)
Basics
Factories/respositories
– factory is a service for manufacturing new instances of an
- bject
– repository is a service for obtaining existing instances of
- bjects
– whether these are the same service is a point of debate
NakedObjects frameworks typically support either style
– most often, objects will be found by navigating from other
- bjects
Pet -> Owner doesn't require the use of a repository, for example
Basics
External services
– bridge to external functionality
such as accessing an email system to send/receive messages
– these will vary in size and shape with the external functionality being accessed/used – look to avoid duplicating Naked Object functionality here!
shouldn't need to write a database access service, for example
Basics
Contributed actions
– a contributed action is an action that is defined on a service but which is presented to the user as an action on an individual domain object (or collection of objects) – these are, effectively, "aspect-oriented" actions applied to Naked Objects
Getting Started
Naked Objects in Java: Apache Isis
Getting Started
Apache Isis
– top-level apache.org project – provides a NakedObjects implementation as Web app – uses HSQL as built-in database
configurable, of course
– uses Apche Shiro for auth/auth
Getting Started
Bootstrap the Maven archetype
– Java latest – Maven latest
Getting Started
Build an empty Isis application
mvn archetype:generate \
- D archetypeGroupId=org.apache.isis.archetype \
- D archetypeArtifactId=simpleapp-archetype \
- D archetypeVersion=1.15.0 \
- D groupId=com.mycompany \
- D artifactId=myapp \
- D version=1.0-SNAPSHOT \
- D archetypeRepository=http://repository-
estatio.forge.cloudbees.com/snapshot/ \
- B
Getting Started
Build and run the app
$ cd myapp $ mvn clean install $ mvn -pl webapp jetty:run
Browse to http://localhost:8080 (login w/ "sven"/"pass")
Labs
Lab 0: Getting Started (15 minutes)
– Install Apache Isis
use the helloworld archetype
– Run the default application
default creds: "sven"/"pass"
Domain Objects
Domain/subject entities in Apache Isis
Isis Domain Objects
Start with "simple" O-O domain class
– identifiable and unique – natural sort order
implements Comparable<T>
– containing (some) business logic – validating rules – ... and so on
Isis Domain Objects
Isis uses Java annotations to carry metadata
– identification of domain entities – presentation customization – persistence customization – ... and so on
Isis Domain Objects
@DomainObject
– identifies that this class is an Isis domain object – annotation data elements control details
editing: whether the instance can be considered immutable
Isis Domain Objects
Domain Objects
@DomainObjectLayout() @DomainObject(auditing = Auditing.ENABLED) public class Convention implements Comparable<Convention> {
Isis Domain Objects
Properties
– typical private-field-public-getter-setter idiom – presumption is that getter and setter "do the right thing"
- r Project Lombok Getter/Setter annotations can generate
methods
– some properties (objects that are value objects) should be @Persistent-annotated
Isis Domain Objects
@Property annotation customizes property details
– editing: whether the instance can be considered immutable
- bjects can still be modified, just no through the UI
– maxLength: maximum length allowed – hidden: indicates where in the UI this property should be hidden
EVERYWHERE, OBJECT_FORMS, PARENTED_TABLES, STANDALONE_TABLES, ALL_TABLES, NOWHERE
– regexPattern/regexPatternFlags: regular expression for validation
Isis Domain Objects
Domain Objects
@javax.jdo.annotations.Column(allowsNull = "false", length = 40) @lombok.NonNull @Property(editing = Editing.DISABLED) @Title(prepend = "Convention: ") @MemberOrder(sequence="1") private String name; public String getName() { return this.name; } public void setName(String val) { this.name = val; }
Isis Domain Objects
Domain Objects
@javax.jdo.annotations.Persistent @javax.jdo.annotations.Column(allowsNull = "true") @Property(editing = Editing.ENABLED) @MemberOrder(name="Dates", sequence="1") private LocalDate start; public LocalDate getStart() { return this.start; } public void setStart(LocalDate val) { this.start = val; // calculate some reasonable values for the other dates } public String validateStart(LocalDate proposed) { // validate the start date is in the future (from now) return null; }
Isis Domain Objects
Properties can be validated
– "validate{propertyName}()" methods – returns a String
- String contents contain message for validation failure
- or return null for acceptance
– see "Classes, Methods, Schema" documentation for full list
Isis Domain Objects
Actions
– typical public method – presumption is that the method does not represent a property or collection – actions often used to allow for restricted editing – actions also often create objects from menu options
Isis Domain Objects
@Action annotation used to customize actions
– restrictTo: whether the action is available in production or just prototyping
NO_RESTRICTIONS,PROTOTYPING
– semantics: provide UI hints about the action's semantics
SAFE_AND_REQUEST_CACHEABLE, SAFE, IDEMPOTENT, IDEMPOTENT_ARE_YOU_SURE, NON_IDEMPOTENT, NON_IDEMPOTENT_ARE_YOU_SURE
– typeOf: type of objects returned from a collection
as a fallback
Isis Domain Objects
Domain Objects
@Action(semantics = SemanticsOf.NON_IDEMPOTENT_ARE_YOU_SURE) public void delete() { final String title = titleService.titleOf(this); messageService.informUser(String.format("'%s' deleted", title)); repositoryService.removeAndFlush(this); }
Isis Domain Objects
Dependent services are injected
– use the javax.inject.Inject annotation to annotate the field – typically the domain object's repository is injected – other services as need demands
Isis Domain Objects
Domain Objects
@javax.inject.Inject @lombok.Getter(AccessLevel.NONE) @lombok.Setter(AccessLevel.NONE) EventRepository eventRepo; @javax.inject.Inject @lombok.Getter(AccessLevel.NONE) @lombok.Setter(AccessLevel.NONE) RepositoryService repositoryService; @javax.inject.Inject @lombok.Getter(AccessLevel.NONE) @lombok.Setter(AccessLevel.NONE) MessageService messageService; @javax.inject.Inject @lombok.Getter(AccessLevel.NONE) @lombok.Setter(AccessLevel.NONE) TitleService titleService;
Isis Domain Objects
Presentation is derived from class
– @DomainObjectLayout annotation can control UI aspects – @MemberOrder controls the order of appearance of properties in the table
name defines grouping ("General" by default), sequence defines order
– @MemberGroupLayout controls the layout of groups of members – object menus derived from actions present on the object – More sophisticated layout can be done via a DomainObject.layout.xml file
initial versions of these can be downloaded from the prototyping UI
Isis Domain Objects
Presentation "hints" can come elsewhere
– title() method returns full object title
- r use @Title to mark which properties provide title
– choices{name}() method(s) returns list of UI choices – autoComplete{name}() provides auto-complete UI behavior – disable{name}() determines whether to disable UI element – hide{name}() determines whether to hide UI element
Isis Domain Objects
Presentation "hints" can come elsewhere
– cssClass() returns CSS class to use
@ActionLayout(), @PropertyLayout or @CollectionLayout can specify CSS to use per-element
– iconName() returns icon (file) name to use
Isis Domain Objects
Object lifecycle
– transient v persistent
- objects begin as transient until persisted
- create objects using container.newTransientInstance(T.class)
– programmatically save using container.persist()
- nce saved, objects save themselves henceforth
Isis Domain Objects
Persistence is handled by JDO
– using the DataNucleus JDO classfile enhancer
- uses JDO annotations to control schema and queries
- does bytecode enhancement at compile-time
– by default, runs in-memory H2 database
- bviously can be configured to use any JDBC database
Isis Domain Objects
JDO class annotations
– @PersistenceCapable: the class can/should be stored
- identityType: How do we want to track identity?
- schema: What schema does this type belong in?
– @DatastoreIdentity: what's the strategy we want to use for OIDs?
- strategy: the strategy (enumeration) to use
- column: the column in which to store the identity
- IdentityType.DATASTORE and "id" are the usual values here
– @Version: how do we want to track instance differences?
- strategy: the strategy (enumeration) to use
- column: the column in which to track the differences
- VersionStrategy.DATE_TIME and "version" are the usual
values here
Isis Domain Objects
JDO class annotations
– @Queries: defining queries that can be invoked
- this is an array of Query annotations
- each Query annotation has a "name" and "value" (being the
query text)
- this is JDOQL, not SQL (which are very similar in syntax)
– @Unique: defining uniqueness constraints on the class/table
Isis Domain Objects
Domain Objects
@javax.jdo.annotations.PersistenceCapable( identityType = IdentityType.DATASTORE, schema = "dragonflight" ) @javax.jdo.annotations.DatastoreIdentity( strategy = IdGeneratorStrategy.IDENTITY, column = "id") @javax.jdo.annotations.Version( strategy= VersionStrategy.DATE_TIME, column ="version") @javax.jdo.annotations.Queries({ @javax.jdo.annotations.Query( name = "findByName", value = "SELECT " + "FROM domainapp.dom.impl.Convention " + "WHERE name.indexOf(:name) >= 0 ") }) @javax.jdo.annotations.Unique(name="Convention_name_UNQ", members = {"name"}) // Should have a query that finds the upcoming one (by date)
Isis Domain Objects
JDO field annotations
– @Column: describes the column-storage details for this field
- allowsNull (boolean): can we store without a value here?
- length (int): storage size of the column (characters)
– @Persistent: indication that the non-primitive field should be stored as a "dependent" value
meaning, a value object with no identity of its own
Isis Domain Objects
Object lifecycle methods
– created()
called when an object has just been created using newTransientInstance()
– loaded()
called when a (persistent) object has just been loaded from the object store.
– persisted()
called when object has just been persisted from the object store.
– persisting()
called when a (not-yet-persistent) object is just about to be persisted from the object store
Isis Domain Objects
Object lifecycle methods
– removed()
called when a (persistent) object has just been deleted from the object store
– removing()
called when a (persistent) object is just about to be deleted from the object store
– updated()
called when a (persistent) object has just been updated in the
- bject store
– updating()
called when a (persistent) object is just about to be updated in the object store
Services
Naked services in Apache Isis
Services
Services are "just" domain objects
– they represent behaviors, not state – they frequently provide entrypoints into the system
such as creating objects
– or they provide access outside of the system
Services
Isis-provided services
– ClockService – ConfigurationService – MessageService – RepositoryService – ServiceRegistry – TitleService – UserService
Services
Isis-provided services
– EventBusService – EmailService – AuditerService – BackgroundService – ... and a lot more
Services
Despite that, you will want to write your own services
– repository services
for creating and finding domain entities
– shared behavior services
behavior that should be shared across non-related domain entities
– "external access" services
behavior to access external systems not already provided by Isis
Services
Services are "just" domain objects
– @DomainService annotation helps discoverability
- nature parameter describes the "visibility" of this service
- VIEW_MENU_ONLY: only appear in the GUI
- VIEW_REST_ONLY: only appear in the Restful Objects viewer
- DOMAIN: only for programmatic use; don't show in UI
Services
Presentation
– By default each domain service corresponds to a single menu item on the menu bar – @DomainServiceLayout can control the grouping and layout
- "primary menu" is the leftmost grouping in the top menubar
- "secondary menu" is the rightmost grouping in the top
menubar
- "tertiary menu" is a menu under the user's name in the
upper-right corner
Labs
Lab 1a: Basic domain entities (60 mins)
– Create domainapp.dom.impl.Customer (domain entity)
- firstName, lastName, street, city, postCode
- just do simple getters/setters for now
– Create domainapp.dom.impl.CustomerRepository (domain service)
create(String fn, String ln), findByLastName(), listAll() (prototyping only)
– Hint: You can use HelloWorldObject/HelloWorldObjects as an exemplar – Run the app to test
mvn jetty:run
Labs
Lab 1b: Basic domain entities (45 mins)
– Make sure nobody lives on "Sassafrass" street
use validateStreet() to prevent this
– Group the fields into two groups
- "General" (firstName, lastName)
- "Address" (street / city / postCode)
- use the @MemberOrder and @MemberGroupLayout
annotation
– Make sure the fields are editable
look at the @Property annotation
Domain object associations
Modeling object relationships in Apache Isis
Associations
Modeling associations in Isis
– mostly a JDO concern
table-to-table relationships
– typically modeled in code using java Collections
recommended to use SortedSet, since that fits the model most closely
Associations
1:m (parent-child) associations
– parent class holds a SortedSet-of-Child field
- annotate with @Persistent
- mappedBy = parent field name in Child
- dependentElement = support cascade deletes?
– child class holds a Parent field
- annotate with @Column
- allowsNull = true (mandatory) or false (optional)
– build the connection by setting the parent in the child
Associations
Parent
public class Parent { // getters and setters omitted @Persistent(mappedBy = "batch", dependentElement = "false") private SortedSet<Child> children = new TreeSet<Child>(); public Parent adoptChild(final Child child) { child.setParent(this); return this; } }
Child
public class Child { // getters and setters omitted @Column(allowNulls = false) private Parent batch; }
Labs
Lab 2: Associations (45 mins)
– "A Customer can have many Orders" – Create domainapp.dom.impl.Order (domain entity)
placed (LocalDateTime), delivery (LocalDateTime), totalAmount (int)
– Define the parent/child relationship from Customer to Order
- Customer (parent)
- Order (child)
- mandatory, and cascading delete
– Add an @Action to Customer to "Place Order"
- two parameters (amount, delivery)
- use the RepositoryService to construct the Order
- use the ClockService to get the current date/time and set