Creating the services
Services do things. That is, services expose common ways of using the object model. A service itself
is a grouping of functionality for a specific purpose.
The BeNOW Service API was written to provide service functionality and is used and exposed by the Web API.
So far, we've implemented the object model for the music library project, and now we'll define the services
for common use of this object model. The services provides access to high level functionality, such
as creating an album, adding tracks, getting albums, etc.
We will eventually be using the music library services during web page construction, but they
should not be specific to anything web related. The web pages will be showing results from
the services, but the services are not tied to the web directly. The services can be used from java directly,
remotely or via a page. We'll see all of these usages, but for now, let's build the services.
Note: Services are more fully described in the Creating Services
and Creating Secure Services
tutorials. The security concepts introduced here may make more sense after reading the Security Tutorial.
Interfaces
The AlbumService defines methods for working with the Albums, and has the following
points of interest
- It
extends Service, which indicates it is a service. The Service API registers and provides access to
services. Only interfaces extending Service are considered. The Service interface
itself is empty, and serves only as an indicator that descendant interfaces are services.
- The
@ParamName("parameter") annotation is provided for each parameter
given in service methods. For example:
public Album createAlbum(@ParamName("title") String title, @ParamName("performer") Performer performer);
The contents of the @ParamName string should be exactly the same as the
name of the java parameter. (The @ParamName is used to map incoming request
parameters to java parameters when invoking the method. As there is no way via java reflection
to determine the name of a parameter, it must be manually specified for the mapping to take
place. Service code parsing to determine parameter names is a planned feature, but for now
specify a @ParamName for each and every service method parameter.)
- The
@Publish annotation is provided on the interface. This annotation is
required for the service to be accepted. Without this annotation, a warning log message
is spewed and the service is not made available by the Service API. Basically, by specifying the
@Publish annotation, the coder has determined that using the service
shouldn't allow dangerous things to be done by inappropriate people and that it's ready
to be used. Think of it as
a 'good to go' check. Removing the @Publish will preventing the loading of the service.
- The
@AnonymousAccess annotation is specified for the service interface,
@AnonymousAccess
public interface AlbumService extends Service { ... }
which allows the service to be accessed by anonymous users.
If this were not provided, anonymous users would not be able to discover or use the AlbumService. Without it, only users with
the doc.walkthru.AlbumService.invoke permission would be able access the service or methods. As
@AnonymousAccess is specified, anonymous users can discover and use the service. Despite being
allowed access, the security is still applied on a method-by-method basis, however. They are only able to discover
or invoke methods also having the @AnonymousAccess annotation, such as the
@AnonymousAccess
public List<Album> getAlbums();
method. If anonymous users, for example, tried to access
@RequiresPermission("modify")
public Album createAlbum(@ParamName("title") String title, @ParamName("performer") Performer performer);
they would be denied, as no @AnonymousAccess annotation is provided. (The explicit
permission asserted in this case is the default service permission of doc.walkthru.AlbumService.modify. If
no permission were specified with @RequiresPermission the implicit permission of
doc.walkthru.AlbumService.invoke would be required.)
- The
@RequiresPermission("delete") annotation is specified in the method:
@RequiresPermission("delete")
public boolean deleteAlbum(@ParamName("album") Album album);
This indicates that doc.walkthru.AlbumService.delete is required
to discover or access the method. Any value specified for @RequiresPermission
will be combined with the full service interface name to create the required permission.
Discovered permissions are automatically available to the security pages, allowing for subsequent
assignment to users. For example, the service
package some.package;
public interface BunnyService extends Service {
@RequiresPermission("fluffyBunny")
public boolean isTooDamnCute();
}
would require that the user have both the some.package.BunnyService.invoke and
some.package.BunnyService.fluffyBunny permissions. Without the some.package.BunnyService.invoke
permission, the service is not discoverable and no method within the service may be invoked.
Without the some.package.BunnyService.fluffyBunny permission, users would not
be able to discover or invoke the isTooDamnCute() method itself. The exception to this is
the 'admin' user, who can do everything (they for whom everything is permissible). After definition, users who should be able
to call isTooDamnCute() can be assigned the some.package.BunnyService.invoke and
some.package.BunnyService.fluffyBunny permissions through the security pages.
In such a way, specific (potentially critical) operations
can be grouped by permissions, which can be used to tie operations to certain types of users.
Say, for example, that a Data Maintainer role was created via the security
pages, and that role had doc.walkthru.AlbumService.delete and doc.walkthru.ProducerService.delete.
If a user had the Data Maintainer role, they would be able to perform delete
methods within the Album and Performer services. The administor (the 'admin' user) can do
everything, of course.
The PerformerService defines methods for working with the Performers, be they Artists or Groups.
The @AnonymousAccess and @RequiresPermission annotations are provided similarly to the
AlbumService.
So, service interfaces are definitions of available functionality, and are regular interfaces which
extend the Service interface and include @ParamName and security
annotations.
At this point, we have definition without implementation, however. Let's complete the services
so that they do something useful.
Implementations
AlbumServiceImpl defines the processing for the methods declared in the AlbumService.
public class AlbumServiceImpl implements AlbumService { ... }
Most of the processing is routine, but it is using the repository. The update()
method of JSQLObject descendants is used to add the object to the repository, as shown
@Override
public Album createAlbum(String title, Performer performer) {
AlbumImpl a = new AlbumImpl(title, performer);
a.update();
return a;
}
and the remove() method is used to remove objects from the repository:
@Override
public boolean deleteAlbum(Album album) {
return ((AlbumImpl) album).remove();
}
@Publish must be provided for the class, as in the service. By providing
the @Publish annotation, you've indicated that the service is ready to be
used.
In the same way, PerformerServiceImpl is an implementation of the PerformerService and
is marked with @Publish, indicating that it is ready to be used.
About the only new thing in the implementation is the use of lists within repository objects. Note
that the object is updated after a contained list is modified:
@Override
public void removePerformerLink(Performer performer, URL link) {
PerformerImpl p = (PerformerImpl) performer;
p.removeLink(link);
p.update();
}
All code, including these new services, can be compiled via ant:
ant jar
For those who have not used ant, it guides compilation and other tasks via it's build file, build.xml.
The standard ant build file used in web projects has the following targets: clean,compile,deploy,
jar and javadoc. Check out the build file and the ant website for more information.
The ant manual can be very useful when
modifying ant build files.
With the services now defined and implemented, the Service API does the rest of the work in publishing and allowing invocation.
Lets now use the services.