ServiceType
,
and the collection of them is the
ServiceType.Registry
.
The standard subclasses are
CompilerType
,
DebuggerType
,
and
Executor
.
Presently there are three varieties of services in the IDE--compiler types, debugger types, and executors. (It is possible for a module to add more to this list, but not a common scenario.) Each variety serves quite a different function in the IDE--the only reason they are grouped together as services is that they share characteristics of needing to be installed, configured, stored, and associated with files. Within each variety, there can be any number of service types (Java classes implementing the abstract class for one of these varieties), each of which is installed with a manifest tag. Finally, each service type has at least one default instance but may have additional instances as configured by the user (or, occasionally, also installed by the module).
The user interface to services is fairly neutral. First of all, the implementation is responsible for providing a way of configuring all registered services somehow. This is currently done by providing nodes under Project Settings (because the list of configured services is part of a project) which use node substructure to display the service varieties, service types, and instances. All of this display is performed according to JavaBeans introspection, which means that implementors of all levels have control over the appearance of these nodes and their behavior towards the user--and this also keeps the API rather thin.
Service instances may be configured by the user as Beans and these changes are saved with the project. Then, services can be
associated with files that they should be applied to; the standard interface to this uses API support classes which display the
current service selection for each variety on the Property Sheet, for every file that can use services.
Creating a Service Type
Creating a service type means creating a new class which subclasses the correct abstract superclass according to the service
variety.
Creating a Subclass
Currently, there are three varieties of service type you can create:
Each variety has at least one abstract method specific to that variety that determines the basic behavior of the service
type. The remainder of the Services API, and of this document, describes generic aspects of service types.
Executor
is used for objects which can run data files in some defined way, such as internal or external or applet execution. Details on
subclassing can be found in the
Execution API.
CompilerType
is used for objects which know how to create the correct compilers to add to a compilation job, e.g. internal Javac compilation,
external process compilation, or RMI stub compilation. Details are in the
Compiler API.
DebuggerType
is used for objects which know how to invoke the IDE debugger correctly according to the type of file, e.g. default debugging,
JPDA debugging, or applet debugging. Details are in the
Debugger API.
Bean Configuration
Every instance of a service type must be configurable as a JavaBean. This means that it should have a list of exposed properties
according to the JavaBeans specification. Writing Bean properties for service types is not difficult; you will need to have
private nontransient serializable fields containing the current configuration, and a list of getter and setter methods to access
this configuration. Property change support is built into ServiceType
, and may be used from subclasses via
ServiceType.firePropertyChange(...)
.
For example:
private String someOption; public String getSomeOption () { return someOption; } public synchronized void setSomeOption (String nue) { if (! isValid (nue)) throw new IllegalArgumentException ("Invalid: " + nue); String old = someOption; someOption = nue; firePropertyChange ("someOption", old, nue); }
BeanDescriptor
with a display name and possibly a short description for the service type. This
descriptor will be used for both the category node (i.e. display of the class itself, rather than any particular instance)
as well as providing information for the instance nodes.
Providing the display name in the descriptor is especially useful as it will also serve as the default for the display name of each instance, though this may be overridden.
ServiceType
, and generally also need to include some properties from a
superclass. For example:
public BeanInfo[] getAdditionalBeanInfo () { try { return new BeanInfo[] { Introspector.getBeanInfo (DirectSuperclass.class) }; } catch (IntrospectionException ie) { ie.printStackTrace (); return null; } }
BeanContext
(in which case the displayed node can contain children according to
the context).
One method which can be overriden in service types is
ServiceType.displayName()
,
which provides a default display name for the service type instance. Generally you do not need to do anything beyond providing a
suitable display name in this method; the standard bean info for ServiceType
exposes the display name (property
name
) as a writable property, modifiable either through the Property Sheet or by directly renaming the
representation node; and when new instances are created under Project Settings, they will automatically be given unique
names if needed. Note that just specifying a display name for the bean descriptor suffices, if you do not
need there to be any difference.
You may override
ServiceType.getHelpCtx()
to provide a context-help association for your service type.
Persistence Considerations
Service types must be serializable--this capability is routinely used when storing the list of service type instances in a
project according to the
registry,
or when simply copying existing instances or creating them from prototype.
The association of service types with files is not done using serialization of the instance itself, however. Rather,
ServiceType.Handle
is used to actually store the reference. This handle currently specifies both the class name of the service type, as well as the
display name of the service instance. When the handle is resolved to a real instance, the lookup is done first by display name,
and if that fails, by retrieving the default instance of the indicated class in the registry. The handle accomplishes two things:
first, it saves space in extended file attributes by not storing the complete instance configuration (which could be somewhat
large); second, it permits users to associate service instances to files by name, so that the instance can be customized in the
registry and the customizations will then apply to all files referring to it.
Like all JavaBeans, service types should have a public default constructor to enable them to be instantiated afresh.
Installation
Installation of service types into the IDE is trivial--you need only add a section to your module's manifest specifying the
service type class according to the
Modules API,
and a default instance of the service type will be created and made available to the user. It is possible to request that your
service type be treated as the IDE default for that variety, unless overridden by data objects with different preferences or
user-defined specific associations; this option is most likely to be useful for debugger types, if you are creating a debugger
implementation and wish to give a standard default style of debugger invocation.
Normally the manifest section specifies the service type class, and the default constructor is called to make the default
instance. However, you may instead specify a serialized instance in the manifest section, according to the JavaBeans
specification, and this preconfigured instance will be installed instead (possibly adding multiple predefined entries to a
category node). This option is especially appropriate for those who only need a single Java class to represent different
configurations used by their module, but wish to install some convenient preconfigured variations.
Using Existing Service Types
Occasionally it may be useful to use existing service types in an unusual way. For example, you may define a service type which
is actually configured with a bean property whose value is a service variety, for purposes of delegation.
Finding Services
Services may be found using the registry (available via
TopManager.getServices()
),
by using the calls
ServiceType.Registry.find(String)
(to find an instance by display name), or
ServiceType.Registry.find(Class)
(to find the default instance of the specified class).
Available services can also be listed using
ServiceType.Registry.services()
;
or more commonly, restricted to some superclass (typically a variety) using
ServiceType.Registry.services(Class)
.
You may also create handles using
new ServiceType.Handle(ServiceType)
,
which creates a small, serializable handle storing the display name and class name, and resolve these handles later (if that is
still possible) using
ServiceType.Handle.getServiceType()
.
Whenever service types are to be stored, it is desirable to use a handle instead of the actual instance.
Varieties of service generally have their own convenience accessors, such as
Executor.executors()
or
Executor.find(String)
,
which just delegate to the more general methods in the service registry.
Finally, the IDE implementation includes standard property editors for ServiceType
as well as all of the standard
varieties, which displays a dialog listing all registered services of that variety, permitting the user to select one, as well as
configure the details of the service instance with a property sheet. Although these property editors are a convenient way to
select service instances, remember that the declared property type they use is ServiceType
or a subclass, while
typically code wishing to store service references will wish to retain only the handle.
Using Service Supports
Most data objects which wish to have one or more varieties of service associated with them persistently will use standard
service supports which are cookie supports available in the APIs. The two existing supports are
ExecSupport
(for both Executor
and DebuggerType
associations) and
CompilerSupport
or one of its inner classes (for CompilerType
associations). These supports implement the appropriate cookie by
delegating the requested operation to the associated service instance, which is stored persistently in the extended attributes of
the data object's primary file, and which may be accessed via e.g.
ExecSupport.getExecutor(MultiDataObject.Entry)
and
ExecSupport.setExecutor(...)
.
Note that data object templates often come with appropriate service instances preconfigured on them, which will be
automatically applied to objects created from these templates. When no specific association has been made for a particular file
(either because the template did not specify one, or because the file in question was created outside the IDE and "discovered" by
it), the support will use a default instance as specified by e.g.
ExecSupport.defaultExecutor()
;
the default implementation will look for the default instance of the default service type for the appropriate variety, but often
data objects (or whatever code is creating a support) will use a customized subclass of the support that overrides this
method. Thus, a typical chain of defaults for a compiler type setting could look like this:
ExternalCompilerType
.
CompilerSupport
specifying as the default internal Javac (rather, the
default instance in the registry of this class).
Service supports will also typically provide methods, e.g.
ExecSupport.addProperties(Sheet.Set)
,
to add one or more properties to a property set permitting the user to configure the desired service instance on the represented
file. Data objects generally use these methods to build their nodes' property sheets.
Creating a New Variety of Service
It is possible for specialized purposes to create a new variety of service, i.e. a direct subclass of ServiceType
that will have concrete service type subclasses. Nothing special is required to install such a new variety--simply begin
installing specific service types into the IDE via module manifest as usual, and appropriate variety and category nodes will
automatically appear under Project Settings. Of course, most of the work is up to you:
ExecSupport
to associate the services to files.
ServiceType
editor, or define your own from scratch.
Built on February 22 2001. | Portions Copyright 1997-2000 Sun Microsystems, Inc. All rights reserved.