|WTP Server Tools API Concepts and Roles|
wst.server API Overview
Last modified Feb 10, 2005
[This document is a work in progress. The document is an attempt to capture the key concepts required for understanding and working with the WTP Server Core API. Note that the concepts may differ subtly from what is currently found in the currently released org.eclipse.wst.server.core plug-in. Once this document is finalized, our intent is to bring the code and specs into line with this document.]
The key concepts for understanding the API (and SPI) are servers and modules. The notions of server runtimes and server configurations play an important, but secondary role.
Not surprisingly, the notion of server is central in the web tools server infrastructure. In this context we are to understand that the server is a web server of some ilk (a more exact definition is not required for our purposes). From a tool-centric point of view, a server is something that the developer is writing "content" for. (In a sense, the server exists, but lacks useful content. The development task is to provide that content.) The content can include anything from simple, static HTML web pages to complex, highly dynamic web applications. In the course of writing and debugging this content, they will want to test their content on a web server, to see how it gets served up. For this they will need to launch a server process running on some host machine (often the local host on which the Eclipse IDE is running), or attach to a server that's already running. And we must arrange that the newly developed content sitting in the developer's workspace ends up in a location and format that a running server can use for its serving purposes.
(The server objects in the API are in fact intermediary "proxy" objects that mediate between the tools running in Eclipse and the real server. A server proxy object has a somewhat independent life from that of a real server. In cases where the server process must be launched, the proxy object exists in advance of the server process, and lives past the termination of the server process. The same server proxy may be serially reused to relaunch server processes. In cases where we are connecting to a server process running on a remote host, the server process may well outlive the proxy.)
Servers have a server runtime. The server runtime corresponds to the installed code base for the server. The main role played by the server runtime is in identifying code libraries to compile or build against. In the case of local servers, the server runtime may play a secondary role of being used to launch the server for testing. Having the server runtimes identified as an entity separate from the server itself facilitates sharing server runtimes between several servers.
Servers have an optional server configuration. The server configuration is information used to configure a running server. Simple types of servers might not require any configuration, whereas full-featured web server have an extensive set of parameters for adjusting the server's behavior. Even though server configuration information usually takes the form of one or more files, configuration information is treated separately from actual content. Actual web content can be deployed on different servers without change, whereas server configuration information is usually highly dependent on the particulars of the server. Having the server configuration identified as an entity separate from the server itself facilitates switching an existing server between configurations, and sharing server configurations between several servers (e.g., a local test server and a remote server running on another host).
The content that is developed for a server comes in modules. A module usually consists of a tree of files; a static HTML web is one simple example. Different types of modules consist of possibly constrained arrangements files of certain file types. The files making up a module under development reside in the Eclipse workspace. In order for a server to access the content within a module, the module in the workspace must be published to the server (we use the term publish rather than deploy which is overused in J2EE). The module is the unit of content that is published to a server. In the case of J2EE servers, the notion of module aligns with the J2EE notion of a module (a server concept). But that needn't be the case; some types of servers, such as a simple HTTP server, don't have a notion of module. Either way, publishing a module to the server entails whatever it takes to get the publishable content from the workspace to the server in a form that the server can access it. Different types of server runtimes are capable of support various module types. Some server types may be able to serve modules directly out of the workspace without copying.
At any given time, the developer may be working with any number of servers. Each server has a set of modules and may have a server configuration. From the complete set of modules in the workspace, any particular server may see only a subset, although it is also possible for all servers to see all available modules. After the developer makes changes to the modules in the workspace and now wants to test their modification, they republish the modules to the server. Depending on the type of server, republishing a module may entail stopping and restarting the server.
Some types of modules can have other modules as children. The standard example of this is a J2EE Enterprise Application (EAR) module, whose children are simple J2EE modules like Enterprise Java Beans and Web Applications. The parent-child relationship is weak: two parent modules can share the same child module, and the same child can be parented by several modules (on the same or on different servers). The file trees for modules are normally mutually disjoint. Module containment is not reflected by any sort of directory nesting. Parent and child modules map to non-overlapping file trees.
Modules typically reside in the workspace. The arrangement is usually that each module exists in a single project, where the content usually corresponds to a particular folder within a project rather than the root of the project. This allows the project to contain other files (e.g., .project, .classpath, source code) that are not considered part of the module content proper. Each different type of module has its own rules for how the content files within it are laid out. For instance, a J2EE web module has the same structure as an unzipped WAR file. (Note that the module structure rules are largely implicit and not reflected directly in the APIs.) It is possible for a project to contains more than one module (although this arrangement is less common, and not as well supported).
During development, projects containing modules can be targeted to a particular server runtime. Targeting allows the IDE to set up various development time properties of the project (such as the backbone of the Java build path) appropriately. The most common scenario is where the project is targeted to the server runtime for the server to which the module is being published. However, it is also possible to target one server runtime (e.g., a generic J2EE 1.3 server runtime) while publishing to a server with a different server runtime (e.g., a Tomcat v5.0 server). Targeting a project to a particular server runtime typically establishes the Java build classpath of the project to include the libraries containing the Java APIs for that server runtime (e.g., the appropriate J2EE class libraries). The Java build classpath also needs to reflect inter-module dependencies; for J2EE module types, the appropriate entries are typically derived from the module's manifest.
The main goals of the Server Core client APIs are to allow clients to create, configure, and launch instances of particular server types. Due to differences in the behavior (and APIs) of different server types, a client must generally be knowledgeable of rules for a particular server type. Clients that have no knowledge of any specific server type are limited to doing things like starting, stopping, and relaunching existing servers. In other words, the clients APIs are generally server-type-specific, rather than sever-type-generic.
The web tools server architecture allows for an open-ended set of server types, server runtime types, and module types. New types are declared via extension points in the Server Core plug-in (org.eclipse.wtp.server.core). Server types are declared by the serverTypes extension point; server runtime types, by runtimeTypes; and module types, by moduleKinds. (Additional extension points are discussed below.) Associated with these extension points are core APIs designed for use by clients contributing to the Server Core extension points; these are more correctly called SPIs (service provider interfaces) to distinguish them from the code designed to be used by other clients (which we refer to here as client APIs).
Many of the extension points provide for a dynamic code component in addition to the static information supplied in the extension declaration. These are termed delegates. For instance, all communication with the running server is handled by a server delegate contributed via the server type extension point. Clients generally deal directly with a layer of objects exposed by the API expressly for client use. In the case of server, clients deal with an IServer. IServer has a fixed, hidden implementation (the internal class Server), which hangs on to a lazily-created server delegate of type IServerDelegate. The IServerDelegate implementation is a server-type-specific object. The more interesting IServer methods (e.g., getServerState()) are handled by the delegate. There are also getDelegate() methods that bridge API to SPI, allowing clients to call methods on the delegate directly. Service providers may also choose to expose their delegate implementation as API. This allows, for example, the provider of a server type to offer additional API that is server-type-specific.
A module factory is responsible for creating modules. There can be any number of registered module factories, declared via the org.eclipse.wtp.server.core.moduleFactory extension point. A module factory may create one or more types of modules (also declared in the extension point). The module factory API does not prescribe a way to create modules; it does, however, provide methods for retrieving extant modules that it was responsible for creating (IModuleFactoryDelegate.getModules() and IModuleFactoryDelegate.getModule(String memento)).
To sync up changes in the local workspace with the server, clients call IServer.publish(IProgressMonitor monitor), which will publish the changes to the server. The server framework handles figuring out which modules have been changed and what resources have changed within the module, but the server delegate is called to do the actual publish operation.
The client API exposes:
The type-level relationships are:
The API exposes server runtime instances as IRuntime. ServerCore.getRuntimes() returns all known server runtime instances. Server runtime instances are created (only) from server runtime types by IRuntimeType.createRuntime(String id), which returns an IRuntimeWorkingCopy. Any number of aspects of the server runtime instance (including location in the file system) can be established or adjusted from their defaults. When editing is complete, IRuntimeWorkingCopy.save(IProgressMonitor monitor) creates, registers, returns a server runtime instance (IRuntime) capturing the desired settings (the working copy is disposed of).
The API exposes server instances as IServer. ServerCore.getServers() returns all known server instances. Server instances are created (only) from server types by IServerType.createServer(String id, IFile file, IRuntime runtime, IProgressMonitor monitor) or IServerType.createServer(String id, IFile file, IRuntime runtime, IProgressMonitor monitor). The file parameter is used to control where the server instance is serialized. Both return an IServerWorkingCopy. Any number of aspects of the server instance (host name, server runtime, server configuration, modules, ...) can be established or adjusted from their defaults. When editing is complete, IServerWorkingCopy.save(IProgressMonitor monitor) creates, registers, returns a server instance (IServer) capturing the desired settings (the working copy is disposed of).
The instance-level relationship between servers and server runtimes and server configurations is exposed at the API: for a given server, IServer.getRuntime() returns the server runtime (IRuntime), IServer.getServerConfiguration() returns the server configuration (IServerConfiguration).
IServer.start(String mode, IProgressMonitor monitor), stop(), and restart(String mode) are used to start, stop, and restart a server, respectively (there are also synchronous methods for starting and stopping). The predicates IServer.canStart(String mode), canRestart(String mode), and canStop() indicate whether the server is receptive to being controlled. IServer.getServerState() can be used to query the overall state of the server (one of starting, started, stopping, stopped, or unknown), and IServer.getModuleState(IModule module) can be used to query the state of any of its modules.