Naked Objects creates the user interface for an application directly from the definitions of the domain model. In this section we will look at that relationship in more detail, with reference to the Expenses Processing example application supplied as part of the download. As we showed in the previous section, any domain model written for Naked Objects may be run with any of the viewers - there is no specific coding required, and the domain model has no knowledge of which viewer is being used. However, each viewer will have different gestures or mechanisms for providing the same functionality. To illustrate this, we will show the same objects being accessed through both the DND and the HTML viewers, side by side.
The application code for the Expenses Processing example, like any Naked Objects application, consists of two things: domain objects and services. The domain objects form the lion's share of that code, so we'll look at how those work first.
The code for examples we will be looking at can be found in the directory examples/expenses/expenses-dom/src in the downloaded files.
The domain objects are the entities - the nouns - that represent the application domain: employee, claim, expense item, project code, currency, and so forth. In the course of using the application, a user will view and manipulate many instances of these domain objects. To understand how Naked Objects handles domain objects, we'll start by looking at an Employee object:
Every object presented in the user interface will have a corresponding Java class in the domain model - in this case it is org.nakedobjects.example.expenses.employee.Employee. Below we can see the code for the Employee object, as presented in Eclipse, with the object's list of methods presented on the left hand side.
The first thing to note is that the type of the object as shown in the user views is derived directly from the class name in Java. The framework inserts spaces before capital letters, so that the class TemporaryEmployee would be presented to the user as 'Temporary Employee'. However we will see later that the name may be over-ridden where necessary, for example if we want the name to include punctuation or other characters not allowed in Java class names. (Note that there is a separate mechanism for dealing with internationalisation).
Secondly, we can see that Employee extends AbstractDomainObject - a class provided within the Naked Objects application library. This is not a requirement: your domain objects may be Plain Old Java Objects (POJOs) - they do not need to extend any class in the framework. However, extending from AbstractDomainObject will save us having to write a few lines of code in each case, as we'll see later.
Note also that in the body of the object we use 'code folding' (the plug-in used here is Coffee Bytes) to break the object's code into regions, each typically containing one or more related methods that together fulfill a high-level responsibility of the object. This is just a coding convention, not a requirement.
In both of the user views of an Employee we can see a field called 'Name'. Within the Employee class there is a Name region of code, expanded here:
// {{ Name
private String name;
@MemberOrder(sequence="1")
@Disabled
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
// }}
The Name region contains a simple property, of type String, defined by a getName and a setName method. This is sufficient information to allow the viewers to display a field containing a textual value. Note that if the property is to be persisted, then it will need both a get and a set method - per the standard JavaBeans convention. If you wish to display a field that is derived automatically from other information in the object, and do not require this to be persisted, then a get alone will suffice.
As with the name of the object, the field name is derived automatically from the name of the property - though we'll see later that this may be over-ridden if needed.
The getName has been marked up with two Java annotations, both defined in the Naked Objects application library. Annotations allow the programmer to enrich the information available to the framework. On properties, any Naked Objects annotations are always associated with the get method. However, annotations are not mandatory - you can write a simple Naked Objects application without using any annotations at all.
By default, any property with both a get and set method will be editable by the user. @Disabled tells the framework that this particular property may never be altered by the user (though it may be altered programmatically). Later we'll see how to make a property modifiable on certain conditions.
@MemberOrder(sequence="1") tells the framework that this property should be the first field displayed in any view of the Employee - irrespective of where it is defined within the code. This ordering information has been observed by both the viewers.
The next region of the code contains another String property, called UserName:
// {{ UserName field
private String userName;
@Hidden
public String getUserName() {
return userName;
}
public void setUserName(final String variable) {
this.userName = variable;
}
// }}
Note that getUserName has been marked up with @Hidden. This tells the framework that this property should never be shown in user views of the object (check this against the two user views above). Later on we'll see how it is possible to hide a property in certain circumstances.
Next we'll look at the EmailAddress region:
// {{ EmailAddress
private String emailAddress;
@MemberOrder(sequence = "2")
@Optional
@RegEx(validation = "(\\w+\\.)*\\w+@(\\w+\\.)+[A-Za-z]+")
public String getEmailAddress() {
return this.emailAddress;
}
public void setEmailAddress(final String emailAddress) {
this.emailAddress = emailAddress;
}
public void modifyEmailAddress(final String emailAddress) {
getRecordActionService().recordFieldChange(this, "Email Address", getEmailAddress(), emailAddress);
setEmailAddress(emailAddress);
}
public void clearEmailAddress() {
getRecordActionService().recordFieldChange(this, "Email Address", getEmailAddress(), "EMPTY");
setEmailAddress(null);
}
public boolean hideEmailAddress() {
return !employeeIsCurrentUser();
}
private boolean employeeIsCurrentUser() {
return getUserFinder().currentUserAsObject() == this;
}
// }}
As well as @MemberOrder, this property is marked up with @Optional and @RegEx annotations. By default, all properties are taken to be mandatory - if the user creates or edits an object then they will be required to specify the contents of each field. @Optional overrides this default behaviour - indicating here that the object may be saved without an email address.
@RegEx is applicable only to String properties. In this case the annotation specifies a Regular Expression that will be used to validate any value that the user types into the field. In a conventional architecture, this functionality would typically be found in the user interface code. The Naked Objects argument is that this functionality should apply to any user interface that might want to change the property, so its proper place is in the object. @RegEx may also be used to reformat a String that has been entered by the user.
The two screens below show how two different viewers make use of the functionality in different ways. In both cases the user has typed in a value that does not match the RegEx specification (they have typed in an email address that contains a space), so the new value has not been accepted or saved.
In addition to getEmailAddress and setEmailAddress, there are modifyEmailAddress, clearEmailAddress and hideEmailAddress methods. Naked Objects recognises the modify, clear and hide prefixes (and a few others that we shall see later) as specifying additional functionality relating to the EmailAddress property.
If a property has a corresponding modify<propertyName> method, then whenever the user modifies the field, this will be called rather than the set. In this case the modify method uses the RecordActionService to record the details of the change, and then calls setEmailAddress to change the value. The reason for adopting this pattern, rather than including the functionality in the set itself, is that the set will be called by the object store each time the object is retrieved. So we use a modify method where we want to do something (such as add to a total) only when the user changes a field.
clearEmailAddress is called, in a similar manner, if the user clears the contents of the field. Again, it is optional - added where we want to perform some logic only when the user clears the property. On the UserName field we saw that @Hidden hides a property from the user permanently. We may, however, want to hide fields under certain circumstances. The visibility of all classes, properties and methods may be controlled via conventional authorization techniques, based on the user's role(s). In rarer cases, we want to control visibility at an instance level. In this case, for privacy reasons we do not want the email address to be visible, except to that person. This is what the hideEmailAddress()method is doing. If the method returns true, the field will be hidden from the user.
Next we will look at the NormalApprover region:
// {{ NormalApprover
private Employee normalApprover;
@MemberOrder(sequence="4")
public Employee getNormalApprover() {
return this.normalApprover;
}
public void setNormalApprover(final Employee normalAuthoriser) {
this.normalApprover = normalAuthoriser;
}
public void modifyNormalApprover(final Employee normalAuthoriser) {
getRecordActionService().recordFieldChange(this, "Normal Approver", getNormalApprover(), normalApprover);
setNormalApprover(normalAuthoriser);
}
public void clearNormalApprover() {
getRecordActionService().recordFieldChange(this, "Normal Approver", getNormalApprover(), "EMPTY");
setNormalApprover(null);
}
public String validateNormalApprover(Employee newApprover) {
return newApprover == this ? CANT_BE_APPROVER_FOR_OWN_CLAIMS: null;
}
public String disableNormalApprover() {
return employeeIsCurrentUser() ? null: NOT_MODIFIABLE;
}
public static final String NOT_MODIFIABLE = "Not modifiable by current user";
public static final String CANT_BE_APPROVER_FOR_OWN_CLAIMS = "Can't be the approver for your own claims";
// }}
The NormalApprover property takes an object of type Employee. Assuming that this field is not disabled, the user may specify an Employee object for this field. Naked Objects will prevent the user from trying to associate the wrong type of object with this field. This is illustrated in the two screens below:
In the left-hand screen (DND) we can see the user dropping an Employee object into the empty field, and the field is flashing green to indicate that this will succeed. If the user attempted to drop another type of object into the empty field, then the field would flash red, and the drop would not update the field. A successful drop will call the set method, or, if a modify<propertyName> method is provided (as it is here), it will call that instead. Note that on the DND viewer, if a field already contains an object, then this may be cleared by right-clicking on that object and selecting 'Clear Association'. This will set the property to null. If there is a clear<propertyName> field (as there is in this example) then that will be called rather than the set method. Alternatively a new reference can be dropped on to the field's label, which combines both the clearing and the subsequent setting of the field.
In the HTML viewer (right-hand screen) drag and drop is not possible. In a reference field such as this one, the user will be given a drop-down list of objects of the appropriate type (i.e. Employees here) that the user has recently viewed. If the required Employee object is not in that list then the user may go and find that object (e.g. from the Employees tab) and then return to the context - this time the newly viewed Employee will have been added to the list automatically. (Note: This is a generic capability provided by the HTML viewer. In other contexts, the programmer may want to specify an explicit list of objects to appear in a drop-down list. This would be achieved by means of a choices<propertyName> method).
The validateNormalApprover method enforces any rules concerning the specific instances of Employee that may be associated with this field. In this particular example, it prevents the user from specifying an Employee as their own approver. Note that this method returns a String. If the specific Employee instance being passed into the method is acceptable, the method should return null; if unacceptable then the method should return a String message that will be made available to the user to advise them why the action will not succeed. (On the DND this appears at the bottom of the screen.)
The disableNormalApprover method prevents the user from modifying the field in certain circumstances. In this example the method enforces the rule that only the Employee themselves may change this field. Like the validate method, it returns a null if the user may modify the field (subject to the validate rules), or returns a String message if they may not. (Note that this method, along with hide (seen earlier) allow for 'instance-based authorization'. Most applications can manage with 'class-based authorization' - in which the classes, properties and actions made available to a user are based on their roles. Class-based authorization in Naked Objects is administered externally to the application and does not require any coding within the domain objects.)
In the next screen we will look at the title region of the Employee object.
// {{ Title
public String title() {
return getName();
}
// }}
The title method specifies the title for the object - which, on both the DND and HTML viewers appears next to the icon. The title is there to help the user identify objects. Naked Objects also provides an easy mechanism to retrieve objects from the object store by their title. Other methods of finding/searching may require repository methods to be written. If no title method is specified, Naked Objects will use the object's toString method as a title. Titles are usually based on one or more of the persisted properties - in this case on the Name. When constructing a title from multiple elements, the Naked Objects application library provides a helper object: TitleBuffer.
The screen below shows the action menu for the Taxi object, as rendered by the two different user interfaces:
By default, any public instance methods on an object, included inherited public methods, will be rendered as a user-action. The exceptions to this rule are:
private, protected, and static methods are ignored by Naked Objects.
For example, the action 'Copy From' on the Taxi object, is derived from this method on the AbstractExpenseItem class (from which Taxi inherits):
@MemberOrder(sequence="5")
public void copyFrom(final ExpenseItem otherItem) {
if (belongsToSameClaim(otherItem)) {
if (dateIncurred == null) {
modifyDateIncurred(otherItem.getDateIncurred());
}
} else if (getClass().isInstance(otherItem)) {
copyAllSameClassFields(otherItem);
}
}
Again, we can see that the method has been marked up with @MemberOrder, which will govern the relative location of this action on the action menu.
Because the copyFrom method takes a parameter, when the user invokes the corresponding menu action they will be presented with a dialog, wherein each of the parameters may be specified. This is shown below on the two user interfaces:
Editing a dialog is similar to editing an object: though there are differences in the way they are rendered (for example a dialog has an 'OK' button in both the DND and HTML user interfaces). Parameters that take value types (such as String or Date) are rendered as fields that the user can type into. Where a parameter is a domain object class or interface, as in this case with ExpenseItem, then the user must specify an object of that type. In the DND user interface, the user may drag and drop an object into the parameter field. In the HTML user interface, the user is automatically presented with a drop-down list of objects of that type that they have recently viewed. If the desired object doesn't appear, they may go and find the object (by navigating from another object, or using a find method on one of the start points) and then return to the dialog, where the recently-located object should now appear on the list.
Adjacent to the copyFrom method on AbstractExpenseItem we can also find the following two methods:
public String disableCopyFrom() {
return disabledIfLocked();
}
public String validateCopyFrom(final ExpenseItem otherItem) {
if (belongsToSameClaim(otherItem) || (getClass().equals(otherItem.getClass()))) {
return null;
}
return COPY_WARN;
}
private final static String COPY_WARN = "Cannot copy";
disableCopyFrom and validateCopyFrom are other examples of recognised methods (see section on recognised methods). They work in a similar manner to the disable<propertyName> and validate<propertyName> methods that we have previously seen - in this case disabling the action under certain conditions, and validating the parameters of the action. For both the user-interfaces shown, disabling the action will result in it being greyed-out on the menu. If the entered set of parameters does not pass the validity test, this will be brought to the user's attention when they attempt to execute the action (e.g. by hitting the OK button), along with an explanatory message.
By default, the user will be required to specify each of the parameters within the dialog. The programmer may, however, use the @Optional annotation in-line (i.e. immediately before any parameter in the method signature) to specify that that parameter may be left empty.
As stated previously all the application code consists either of domain objects or services, with the former typically representing the lions share of the code. Now we'll look at the services.
Services perform two roles in a Naked Objects application. First, they provide a place to put functionality that cannot be placed on an instance of a domain object, of which the two most obvious examples are:
To fulfill these requirements we could create two separate services, called, say, CustomerFinder and CustomerFactory. Or we could create a single service called, say, Customers, which has methods to cover both requirements. There's no hard-and-fast rule about how services should be partitioned.
The second role that services perform within a Naked Objects application is to bridge domains. The following are examples of what we mean by bridging domains:
In this section we'll look at how services are defined, and in the next section at how they are used.
Services are implemented as Java classes, as are domain objects, but they are handled differently by the framework.
It is good practice to define services as Java interfaces. That way it is possible for the implementation of the service to change over time, without affecting any of the objects that use the service. During development it is often useful to develop a simple 'mock' implementation of a service that can be used either for prototyping or testing purposes; this can then be replaced with a proper implementation as development progresses towards deployment. For example, within the Expenses Processing application, the following Java interface defines a service for sending an email:
package org.nakedobjects.example.expenses.services;
public interface EmailSender {
void sendTextEmail(final String toEmailAddress, final String text);
}
This service definition has just one method, but it could easily have more, such as methods that take a List of recipient addresses, or that can accommodate file attachments. JavaMailSender is an implementation of that service:
public class JavaMailSender extends AbstractService implements EmailSender {
private static final String SMTP_HOST_NAME = "localhost";
private static final String SMTP_AUTH_USER = "expenses@donotreply.org";
private static final String SMTP_AUTH_PWD = "";
private static final boolean authenticate = false;
private class SMTPAuthenticator extends javax.mail.Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
final String username = SMTP_AUTH_USER;
final String password = SMTP_AUTH_PWD;
return new PasswordAuthentication(username, password);
}
}
public void sendTextEmail(final String toEmailAddress, final String text) {
try {
final Properties properties = new Properties();
properties.put("mail.smtp.host", SMTP_HOST_NAME);
properties.put("mail.smtp.auth", authenticate ? "true" : "false");
final Authenticator authenticator = authenticate ? new SMTPAuthenticator() : null;
final Session session = Session.getDefaultInstance(properties, authenticator);
final Message message = new MimeMessage(session);
final InternetAddress fromAddress = new InternetAddress(SMTP_AUTH_USER);
final InternetAddress toAddress = new InternetAddress(toEmailAddress);
message.setFrom(fromAddress);
message.setRecipient(Message.RecipientType.TO, toAddress);
message.setSubject("Expenses notification");
message.setContent(text, "text/plain");
Transport.send(message);
} catch (AddressException e) {
throw new ApplicationException("Invalid email address", e);
} catch (MessagingException e) {
throw new ApplicationException("Problem sending email", e);
}
}
}
We can see that this service performs a technical bridging role: it bridges between the object domain and an external SMTP server.
Since there could be multiple implementations of any one service within our code base, Naked Objects needs to be informed of which services it is to reference when running an application. This is done within the properties files. For example, the nakedobjects.properties file, which may be found within the expenses.app.client\config directory, contains the property specification:
nakedobjects.services.prefix=org.nakedobjects.example.expenses nakedobjects.services=services.JavaMailSender
This specifies that the class JavaMailSender is to be referenced as a service within the application. You will find a list of other services being referenced there also. Many of those services are 'repositories', and though there is no technical difference between a repository and any other kind of service, repositories play such an important role in Naked Objects applications, that they are worth exploring in more detail.
Naked Objects handles the basic object lifecycle (create, read, update, delete) automatically - there is no need to define your own methods for saving or updating objects, or for retrieving an object that you have a reference to. These mechanisms work the same way irrespective of what technology you are using to persist the objects - such as via Hibernate, natively to a relational database, or via the 'XML Object Store'.
Naked Objects even provides some simple mechanisms for searching for persisted objects - that also operate the same way, irrespective of the object store. However, a business application will also need more complex search queries that, for reasons of efficiency, will need to be written specifically for the type of object store you are working with.
Best practice in application design suggests that such queries should be implemented on 'Repository' classes, rather than within the domain classes directly. That way if you change the persistent object store, you can just create a new implementation of the affected Repositories, without having to change any domain classes. Naked Objects supports this concept. Within the Expenses application you will find the following three repository definitions:
org.nakedobjects.example.expenses.claims.ClaimRepository org.nakedobjects.example.expenses.employee.EmployeeRepository; org.nakedobjects.example.expenses.recordedAction.impl.RecordedActionRepository;
In each case the repository is defined as a Java interface, anticipating the possibility of different implementations. We'll look at the ClaimRepository definition:
public interface ClaimRepository {
final static int MAX_CLAIMS = 20;
final static int MAX_ITEMS = 10;
List<Claim> findClaims(final Employee employee, final ClaimStatus status, final String description);
List<Claim> findRecentClaims(final Employee employee);
boolean descriptionIsUniqueForClaimant(final Employee employee, final String initialDescription);
List<ExpenseItem> findExpenseItemsLike(final ExpenseItem item);
List<Claim> findClaimsAwaitingApprovalBy(Employee approver);
ClaimStatus findClaimStatus(String title);
ExpenseItemStatus findExpenseItemStatus(String title);
}
This interface defines some seven method signatures for retrieving Claims and ExpenseItems. Note that there is no hard rule about the scope of a single Repository - we could have decided to separate this into a ClaimRepository and an ExpenseItemRepository if that offered us some advantage.
The example application contains two concrete implementations of ClaimRepository:
org.nakedobjects.example.expenses.services.inmemory.ClaimRepositoryInMemory org.nakedobjects.example.expenses.services.hibernate.ClaimRepositoryHibernate
The first of these is intended for use with a standalone prototype - with a relatively small number of object instances, all held in memory. So the finder methods can be written 'naively' - to enumerate through all the objects in a class and find the match(es). The following is its implementation of the findClaimsAwaitingApprovalBy method:
public List<Claim> findClaimsAwaitingApprovalBy( final Employee approver ) {
return allMatches(
Claim.class,
new Filter() {
public boolean accept(final Object obj) {
Claim claim = (Claim) obj;
return claim.getStatus().isSubmitted() && claim.getApprover() == approver;
}
});
}
This delegates to an allMatches method, inherited from AbstractFactoryAndRepository, and use a Filter object (created in-line) to compare to each instance of Claim held in memory. Such methods are very simple to write and debug (because they can invoke methods on the objects being searched, such as isSubmitted here), but they would not operate efficiently for large numbers of objects.
ClaimRepositoryHibernate is written to work with the Hibernate Object Store and can work efficiently at large scale. Here is its the findClaimsAwaitingApprovalBy method:
public List<Claim> findClaimsAwaitingApprovalBy( final Employee approver ) {
final Criteria criteria = hibernateHelper.createCriteria(Claim.class);
criteria.
add(Restrictions.eq("approver", approver)).
createCriteria("status").
add(Restrictions.eq("titleString", ClaimStatus.SUBMITTED));
return hibernateHelper.findByCriteria(criteria, Claim.class);
}
This implementation uses a Criteria object, a class provided by the Hibernate framework.
Both ClaimRepositoryInMemory and ClaimRepositoryHibernate inherit from ClaimRepositoryAbstract, which inherits from AbstractFactoryAndRepository and also implements the ClaimRepository interface. This pattern is not a requirement - the implementations do not need to inherit from any framework class, they can just implement the required Repository interface natively. However the advantage of this pattern is that some simple query methods can be written generically, as shown in these two examples:
public List<ExpenseItem> findExpenseItemsOfType(final Employee employee, final ExpenseType type) {
final List<Claim> claims = findClaims(employee, null, null);
final List<ExpenseItem> items = new ArrayList<ExpenseItem>();
for (final Claim claim : claims) {
ExpenseItem pattern = (ExpenseItem) newTransientInstance((Class) type.correspondingClass());
pattern.setClaim(claim);
List list = (List) uniqueMatch((Class) type.correspondingClass(), pattern, EXCLUDING_SUBCLASSES);
items.addAll(list);
}
return items;
}
public ClaimStatus findClaimStatus(String title) {
return uniqueMatch(ClaimStatus.class, title, EXCLUDING_SUBCLASSES);
}
These two query methods both delegate to uniqueMatch, inherited from AbstractFactoryAndRepository, but different, overloaded, versions of that method. findExpenseItemsOfType invokes uniqueMatch with a pattern - an instance of ExpenseItem that has been set up with the fields where a match is required. findClaimStatus invokes uniqueMatch with a String representing the title of the object required. The implementation of both of these forms of query is delegated to the object store, in a manner that is transparent to the application programmer. So, if the nature of the query can be represented in the form of a find by title, or a find by pattern, then it is advantageous to use these methods on AbstractFactoryAndRepository. Otherwise you can write specialised methods on the respective repository implementations.
As with all services, we need to inform the framework of the existence and intent of these implementations, via the properties files. Within nakedobjects.properties you will find:
nakedobjects.services=services.inmemory.ClaimRepositoryInMemory
and within persistor_hibernate.properties you will find:
nakedobjects.services = services.hibernate.ClaimRepositoryHibernate
persistor_hibernate.properties is only referenced if the application is run with the Hibernate Object Store, in which case the framework will recognise that ClaimRepositoryHibernate is intended to replace ClaimRepositoryInMemory as the implementation to use.
A Factory is just the name we give to a kind of service that specialises in the creation of new objects, of one or more kinds. It is not necessary to use a Factory in order to create objects within Naked Objects: we may invoke the methods newTransientInstance from within a method on a domain object or within any service.
However, if there is a need to create a type of object from several different places in the application, and there are common steps involved, then it is good practice to delegate this to a Factory. Within Naked Objects a Factory is just another service, it doesn't have any special status. For example, within the Expenses application, new Claims and new ExpenseItems are created via the ClaimFactory. However, new RecordedActions are created in the RecordedActionService. Note that ClaimFactory is specified as a class rather than an interface, because we have no particular reason to anticipate different implementations of the factory.
Services are used within Naked Objects in three ways:
We'll look at these three in turn.
Objects may need access to services, such as repositories for finding related objects, or for calling functionality from outside the domain model. Naked Objects uses the 'dependency injection' model. Each object merely needs to provide a set method for each type of service that it requires. For example, within the Employee object there is a code region labelled Injected Services:
// {{ Injected Services
// {{ Injected: RecordActionService
private RecordActionService recordActionService;
protected RecordActionService getRecordActionService() {
return this.recordActionService;
}
public void setRecordActionService(final RecordActionService recordActionService) {
this.recordActionService = recordActionService;
}
// }}
// {{ Injected: UserFinder
private UserFinder userFinder;
protected UserFinder getUserFinder() {
return this.userFinder;
}
public void setUserFinder(final UserFinder userFinder) {
this.userFinder = userFinder;
}
// }}
// }}
In this case, the Employee object has specified that it requires two services to be injected: a RecordActionService and a UserFinder. Whenever an instance of Employee is created, or retrieved from the object store, Naked Objects will inject the implementation that it knows about (as specified in properties) for each type of service required. Note that, unlike the other properties we have looked at, get methods may be protected, because the property is not displayed. (Strictly speaking a get is often not needed here - as the injected service may be accessed via the variable - but it is considered to be good practice.)
From within the object we can then call any of the methods defined for those types of service. For example, we can see that the hideEmailAddress method makes a call (via employeeIsCurrentUser) to the UserFinder service:
public boolean hideEmailAddress() {
return !employeeIsCurrentUser();
}
private boolean employeeIsCurrentUser() {
return getUserFinder().currentUserAsObject() == this;
}
Services may be made available directly to the user. On the DND user interface these appear as the large icons on the desktop; on the HTML user interface (that is, as styled by the default CSS) these appear as the tabs across the top of the screen. Which services are made available to a particular user are defined in 'perspectives' within a user profile. Within the Fixture project the class ExplorationUserProfileFixture defines the perspectives for various defined prototype users:
public class ExplorationUserProfileFixture extends UserProfileFixture {
@Override
protected void installProfiles() {
...
Profile svenProfile = newUserProfile();
Perspective claimsPerspective = svenProfile.newPerspective("Claims");
claimsPerspective.addToServices(Claims.class);
claimsPerspective.addToServices(Employees.class);
saveForUser("sven", svenProfile);
...
}
}
The above example specifies that the user 'sven' is to be given a perspective called 'Claims', which gives him direct access to two services: ClaimStartPoints and EmployeeStartPoints. If we look at the second of those, we can see that it defines two actions: findEmployeeByName and me:
@Named("Employees")
public class EmployeeStartPoints extends AbstractService {
// {{ Title & ID
// {{ Injected Services
@MemberOrder(sequence = "2")
public List<Employee> findEmployeeByName(@Named("Name (or start of Name)")
final String name) {
List<Employee> results = employeeRepository.findEmployeeByName(name);
if (results.isEmpty()) {
warnUser("No employees found matching name: " + name);
return null;
}
return results;
}
@Executed(Executed.Where.LOCALLY)
public Employee me() {
Employee me = employeeRepository.me();
if (me == null) {
warnUser("No Employee representing current user");
}
return me;
}
}
Both of these methods delegate to methods on the EmployeeRepository, which has been injected (services may be injected into other services, just as into domain objects). Note that it is not necessary to define specific services to be provided directly to the user - we could provide the user with direct access to the Repositories, Factories or other services specified within the application. Creating dedicated user-oriented service definitions just helps us to separate the concerns. Calling them 'Start Points' is also just a convention.
The screens below show the action menu on the Claim object, as rendered by the two different user interfaces:
This menu has a sub-menu, entitled 'Recorded Actions', containing, in this case, a single method 'All Recorded Actions'. Sub-menus in Naked Objects are 'contributed' by services; the actions in the sub-menus are described as 'contributed actions'. In this case the actions are contributed the service RecordedActionContributedActions:
@Named("Recorded Actions")
public class RecordedActionContributedActions extends AbstractService {
// {{ Injected Services
public List<RecordedAction> allRecordedActions(RecordedActionContext context) {
return recordedActionRepository.allRecordedActions(context);
}
}
The method allRecordedActions takes a RecordedActionContext as a parameter, and will return all the RecordedActions associated with that object. Note that RecordedActionContext is an interface that defines no methods - it is purely a type definition:
public interface RecordedActionContext {
}
However, this interface is implemented by two classes: Employee and Claim. The net result of this is that the action 'All Recorded Actions' will be contributed to each instance of Employee and of Claim. By default, this would appear in a sub-menu named after the service on which the method was defined (i.e. 'Recorded Action Contributed Actions'), but in this case we have used the @Named annotation to override this and render the service name, and hence the sub-menu name, simply as 'Recorded Actions'.
We can also see that this method delegates its execution to the RecordedActionRepository, which has been injected as a service. You are not required to follow this pattern, or this naming convention. In fact, if the allRecordedActions method on RecordedActionRepository was not @Hidden, then it would have been contributed automatically - without the need for defining RecordedActionContributedActions. We have defined the latter purely to help convey intent and manage our code base.
The rule is that any method defined on any service that the user is authorised to access (see section on authorization) and is not hidden, will be contributed to any object of a type that features as any of the parameters to that method.
This is a very powerful feature of Naked Objects, but it is one that takes a bit of getting used to. In some respects it is a little bit like Aspect Oriented Programming (AOP), in that it allows an object effectively to inherit capabilities from several different sources. However, this all takes place at run-time, not at compile time.
In a more complex application, it might well be that a domain object might have several contributed sub-menus, each containing several methods. Designing an application this way allows us to keep the model well partitioned. In this very simple example, it has allowed us to keep the part of the model concerned with recording actions very separate from the other parts of the model.
Fixtures are used to set up objects within the code based, principally for use within prototyping and or testing. Naked Objects provides specific support for using fixtures. The following code shows a fixture class that sets up one claim:
public class SvenClaim1NewStatus extends AbstractClaimFixture {
public static Employee SVEN;
public static Employee DICK;
public static Claim SVEN_CLAIM_1;
@Override
public void install() {
SVEN = EmployeeFixture.SVEN;
DICK = EmployeeFixture.DICK;
SVEN_CLAIM_1 =createNewClaim(SVEN, DICK, "28th Mar - Sales call, London", ProjectCodeFixture.CODE1, new Date(2007,4,3));
Date mar28th = new Date(2007,3,28);
addTaxi(SVEN_CLAIM_1, mar28th, null, 8.50, "Euston", "Mayfair", false);
addMeal(SVEN_CLAIM_1, mar28th, "Lunch with client", 31.90);
addTaxi(SVEN_CLAIM_1, mar28th, null, 11.00, "Mayfair", "City", false);
}
}
This inherits from AbstractClaimFixture, which provides the helper methods such as createNewClaim, and which inherits in turn from AbstractFixture, a class in the Naked Objects application library. However, there is no need to follow this pattern: a fixture may be any class that has an install method.
Fixtures may be composite, as we can see in this example:
public class SvenClaims_All extends AbstractClaimFixture {
public SvenClaims_All() {
addFixture(new SvenClaim1NewStatus());
addFixture(new SvenClaim2Submitted());
addFixture(new SvenClaim5New());
addFixture(new SvenClaim3Returned());
addFixture(new SvenClaim4Approved());
}
public void install() {}
}
This fixture has had five other fixtures added to it. The install method is empty: Naked Objects will automatically call install on each of the fixtures that has been added to this composite fixture. This pattern makes it easy to manage large fixtures, and multiple sets of (potentially overlapping) fixtures, both for prototyping and for testing.
As with services, Naked Objects needs to be instructed which fixtures it should use when running an application. This may be done in the properties files, for example:
nakedobjects.fixtures.prefix=org.nakedobjects.example.expenses.fixtures nakedobjects.fixtures=ExplorationPerspectiveFixture, RefdataFixture, EmployeeFixture, SvenClaims_All
Note that this also specifies the ExplorationPerspectiveFixture, which we looked at earlier.
Fixtures may also be specified as a command line parameter (see section on command line parameters) when launching the application from the command line; composite fixtures are especially handy in this circumstance.