a Naked Objects application for enterprise use

Assuming that you have developed your domain model and tested it as a standalone prototype (previous section), this section describes the process of deploying the domain object model as an enterprise application.

Running Client-Server over Sockets

Running Naked Objects as a socket server

$ nakedobjects.sh --type server --viewer xstream-sockets --persistor xml

This command runs Naked Objects as a socket server. The viewer command line option specifies that the server will listen for requests using basic TCP/IP plain sockets with the XStream marshalling mechanism. The persistor command line option indicates to use XML object store as the persistence mechanism.

Running Naked Objects as a socket client

$ nakedobjects.sh --type client --viewer dnd --connector xstream-sockets 

This commands starts up Naked Objects as a client, using the DND viewer. The connector command line option indicates to use the XStream marshalling mechanism over basic TCP/IP sockets. As we are now running a multi-user system more than one client can be started up.

To allow the client to connect to a remote server the address property needs to be specified. For simplicity this can be added the nakedobjects.properties file, but it may alternatively be placed in a separate file called transport_sockets.properties.

nakedobjects.transport.sockets.host = server.mycompanyname.com

Running Client-Server over HTTP

Running Naked Objects as a HTTP server

$ nakedobjects.sh --type server --viewer encoding-http --persistor xml

This command runs Naked Objects as a server. The viewer command line parameter specifies that the server will listen using http using the encoding marshalling mechanism. The persistor property XML object store as the persistence mechanism - a very simple persistence mechanism useful for initial prototyping. (In fact, the XML object store is the default persistence mechanism, so this property may be omitted from the command).

Alternatively the remoting servlet EncodingOverHttpRemotingServlet can be added to the web.xml in the webapp project and the Naked Objects run as a web app, see below.

Running Naked Objects as socket client

$ nakedobjects.sh --type client --viewer dnd --connector encoding-http 

This commands starts up Naked Objects as a client, using the DND viewer. The connector command line option indicates to use submit requests using http and with the encoding marshalling mechanism. Again, as we are now running a multi-user system more than one client can be started up.

To allow the client to connect to a remote server the address property needs to be specified. For simplicity this can be added the nakedobjects.properties file, but it may alternatively be placed in a separate file called transport_http.properties.

nakedobjects.transport.http.url = http://server.mycompanyname.com:8080/remoting.svc

Running as a WebApp

Naked Objects provides three different ways to run as a webapp:

Using nakedobjects.sh

$ nakedobjects.sh --type server --viewer html --persistor xml
2007-08-09 12:37:13.671::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
2007-08-09 12:37:13.801::INFO:  jetty-6.0.2
2007-08-09 12:37:13.954::INFO:  Started SocketConnector @ 0.0.0.0:8080

This command runs the Naked Objects with the HTML viewer, allowing multiple clients to access it via a browser. As for the standalone version the users need to access the URL http://server/logon.app to access the log on page.

Using WebServer bootstrap

The next mechanism uses the org.nakedobjects.webserver.WebServer bootstrap to run Naked Objects. This loads up whatever is in the webapp project's web.xml file.

TODO: we don't have a webserver.sh script to show this in action; we probably should.

Ordinarily the web.xml will be configured to run the same HTML viewer, so the end result will be the same. However, if necessary the remoting servlet can also be configured

Deploying as a WAR

The final mechanism is to use Maven to package up the webapp project as a WAR file, packaging up whatever is in the web.xml file. As above, ordinarily the web.xml will be configured to run the same HTML viewer, so the end result will be the same. However, if necessary the remoting servlet can also be configured

Packaging up is done using:

$ cd webapp
$ mvn clean package

This should result in a WAR file in target directory. This can be deployed to an existing servlet containerd

Setting up perspectives

Perspectives allow the set of services available to a user (eg as icons in the DnD viewer) to be customized for that user. Since these services represent the "start points" for the user to interact with the domain model, they in a sense define an application on a per-user basis.

The perspectives are stored in user-profiles, which are a persistence mechanism independent of the object store. (The intent is for user profiles to store additional information for use by viewers, for example allowing a user to reskin an application or change colors or fonts. As of NOF 4.0 the functionality provided is still limited, however).

The key here is that if a user logs in and no perspective exists for that user then one will be created for them. The new perspective will be a copy of the 'template' perspective, or, if none was defined, a perspective containing all the known services. To create a template perspective add a perspective fixture that calls saveAsDefault(), rather than saveForUser() for a named user, as shown below.

public class PerspectivesFixture extends UserProfileFixture {
    @Override
    protected void installProfiles() {
        Profile profile = newUserProfile();
        Perspective perspective = profile.newPerspective("ECS");
        perspective.addToServices(LocationFactory.class);
        perspective.addToServices(CustomerRepository.class);
        perspective.addToServices(PaymentMethodFactory.class);
        
        saveAsDefault(profile);
    }
}

With that set up, when a new user now logs in they will see three service icons on the screen for locations, customers and payment methods. On the drag and drop user interface the user can add and remove services from their perspective. To remove a service icon right-click on the grey border and select the close option. To add a service select the Services... option from the application menu (accessed by right-clicking on the application background) and drag the required service onto the desktop. Unfortunately there is no similar mechanism available on the HTML user interface so you will need to modify the created perspectives via the DND UI.

Adding authentication and authorization

TODO: this material seems to repeat a lot of what is in "managing_security", should be merged and simplified.

Starting Naked Objects requires a user name and password . (The exception is when running in exploration mode, which uses default values). The simplest way to specify the list of user names and passwords is in a passwords file.

Authentication using a passwords file

Create a file called passwords, and place into the config directory as the following example shows.

config/
   passwords

The following example shows how user names (sven and dick) and corresponding passwords (passwd1 and passwd2) should be specified in the password file.

sven:passwd1
dick:passwd2

Authorization using Allow and/or Disallow files

Authorization allows access to actions and properties to be controlled by configuration. Authorization is entirely optional and is disabled by default - in which case every user is given access to all actions and properties. A prerequisite for authorization is for users to have roles. These should be added to the password file as follows.

sven:passwd1:ordinary_user
dick:passwd2:special_user|admin

Sven has the role 'ordinary_user', dick has the two roles 'special_user' and 'admin'.

Naked Objects needs to be told which actions and properties are authorised for which roles. The simplest way to do this is in a file i.e. allow in the config directory.

config/
   password
      allow
   

Each entry in the allow file specifies either a class, or a particular action or property within a class, together with the roles that are allowed to access it. E.g.

example.dom.PhoneNumber#Number:ordinary_user|special_user
example.dom.Contact#Phones:ordinary_user|special_user
example.dom.Contact#FullName:ordinary_user|special_user
org.nakedobjects.nof.core.service.SimpleRepository#FindByTitle(java.lang.String):ordinary_user|special_user
example.dom.PhoneNumber#Type:ordinary_user|special_user
example.dom.Contact#KnownAs:ordinary_user|special_user
org.nakedobjects.nof.core.service.SimpleRepository#NewPersistentInstance():ordinary_user|special_user
example.dom.Contact#CreatePhone():special_user
org.nakedobjects.nof.core.service.SimpleRepository#NewTransientInstance():ordinary_user|special_user
org.nakedobjects.nof.core.service.SimpleRepository#AllInstances():ordinary_user|special_user

The above file allows 'example.dom.Contact#CreatePhone()' only for users with role 'special_user' all other actions and properties are allowed for roles 'ordinary_user' and 'special_user'. A different, terser implementation of this would be to use separate allow and disallow files, or example, the allow file:

example.dom.PhoneNumber:ordinary_user|special_user
example.dom.Contact:ordinary_user|special_user
org.nakedobjects.nof.core.service.SimpleRepository:ordinary_user|special_user

and the disallow file:

example.dom.Contact#CreatePhone():ordinary_user

For more details, see the section on Managing Security.

Configuration

No configuration is necessary for the simplest (file-based) authentication mechanism. For authorization the reflector needs to be decorated to look up the permissions every time the framework needs information about the visibility of fields and menus. To include the decorator add the following to a properties file.

nakedobjects.reflector.facet-decorators=file-security

If a disallow file is to be used, this needs to be specified explicitly (the allow file will be picked up automatically):

nakedobjects.security.blacklist.file=config/disallow

Adding support for internationalisation

All the text that appears to the user comes from the domain object model. Any literal text used within the code may be externalised, and localised using the standard Java mechanism of resource bundles. However as all field and menu names are derived from the method names the framework provides a mechanism to adapt these names using resource bundles as well.

Localisation files

Create a file for each required language, e.g., i18n_en_GB.properties for English in Great Britain, and place them into the project such that they will be included in the class path. For the project structures we have already seen the files could be added to the resources directory, as the following example shows.

resources/
   i18n_en_GB.properties
   i18n_de_DE.properties
   i18n_fr_FR.properties
   

The following example is part of one of those translation file and shows how property and action names and descriptions may be specified.

example.dom.Contact.property.Phone.name=Téléphone
example.dom.Contact.action.NewPhone.name=Nouveau téléphone
example.dom.Contact.property.FullName.description=Le nom complet du client

For more details on defining localisation files, see section about I18N files.

Configuration

The reflector needs to be decorated with a facet decorator to look up the translated names every time the framework needs information about the fields and menus. To include the decorator add the following to a properties file.

nakedobjects.reflector.facet-decorators=resource-i18n

If the application is being accessed via the web (html) viewer, then the server will need to perform the localisation, so the above property should be included in one of nakedobjects.properties read by the server; if the application is being accessed by a client (e.g. the DND viewer) then the localisation will be performed within the client, and the property should be specified in one of the properties files read by the client e.g. facet-decorator_i18n.properties.

The locale will be picked up automatically from the machine on which the code is executing. You may override this by specifying the locale in a properties file.