Skip to content

Workspace

MrSnyder edited this page Apr 11, 2013 · 20 revisions

This page is the central entry point for documentation about the deegree workspace concept and implementation.

Shortcomings of old workspace concept

The old workspace concept had a couple of shortcomings, which I'll describe here to give an understanding why a new implementation has been started.

Dependencies

The workspace has a concept of dependencies, but dependencies are only possible on a resource type level. At first glance, this is sufficient in most cases, but not all.

The first resource we came across where this was not sufficient any more was the caching tile store, which naturally depends on another tile store.

Thinking about it, a tile store implementation that automatically tiles a layer resource on the fly/whatever would be a natural thing. But since there's also a tile layer implementation, the layer subsystem already depends on the tile subsystem, introducing a dependency the other way round would introduce a cycle.

Restartability

Restarting a single resource is possible, but often has unknown/bad side effects on other resources. So if you restart your feature store (possibly changing the configuration) affects eg. the WFS, some feature layer, the WMS...

The only workable solution has been to just restart the whole workspace. While that's sufficient in many cases, it can be slow and time consuming, especially if many resources are configured.

If the exact dependency chains between resources were known, only affected resources could be restarted.

Static is bad

Some resource managers do things in a static context. While this is often sufficient in a webapp (where only a single workspace is active anyway), it's still bad practice and should be avoided. Looking at the infamous ConnectionManager, a rewrite is badly needed.

XML

The workspace is tightly bound to the XML formats which are used to configure resources. While this is not necessarily a bad thing, other scenarios still seem useful, where configurations are created by some other means (such as reading the config from a database). This is just not possible to do with the current workspace concepts.

Basic workspace concepts

Resources

So, let's have a look at the basics. The workspace revolves around resources. A resource is an instance of a specific class of object deegree works with. Example: a feature store is a resource, a WFS is a resource, a layer is a resource, a JDBC connection is a resource.

Resources are grouped together by their type. So all feature stores are in a group, all services are in a group, all layers, all JDBC connections etc. The workspace is responsible to manage these resources, and provide access to them.

The workspace

The workspace manages resources, and provides access to them. That means that the lifecycle of resources is controlled by the workspace.

Typically your interaction with the workspace will be to initialize it, and to access resources contained within.

Resources are identified by ResourceIdentifier objects. This is a two part or qualified identifier, consisting of a string ID (usually the basename of the .xml configuration file) and a provider class.

We'll have a look at the provider classes later.

The lifecycle

In order to understand how the workspace works internally, you'll need to understand the lifecycle a resource goes through. We've got a couple of phases:

  • scanning
  • preparing
  • building
  • initializing

In the scanning phase, the workspace finds resources. This results in a bunch of ResourceMetadata objects. These metadata objects are uninitialized.

In the preparing phase, which operates on ResourceMetadata objects, the resources determine what they need to be built. This includes finding out what other resources they depend on. This results in the ResourceMetadata to be initialized, and a bunch of ResourceBuilder objects. After this phase the ResourceMetadata can be sorted, taking into account their dependencies.

In the building phase, the ResourceBuilder objects are used actually build Resource objects, the result is a bunch of uninitialized Resource objects.

In the initialization phase, the Resource objects are being initialized. After this, they can be properly used.

The bigger picture

This section introduces a couple of interfaces, and explains what implementations are responsible for.

Some interfaces have been introduced above in the lifecycle section.

ResourceLocation objects are an abstraction of where the configuration files live. Instead of URL or File objects they are used to obtain the actual configuration file content. This allows for other implementations that eg. pull the files off the net or so.

ResourceManager objects are responsible for initially creating ResourceMetadata objects during the scanning phase, and for knowing which ResourceProvider implementations are available.

ResourceProvider objects are used by the manager to actually create a ResourceMetadata object for a resource. For each type of resource, there must be an abstract class or interface that all concrete providers implement. This serves as an SPI extension point (so the resource manager knows who can create metadata objects for a given location). The abstract ResourceProvider for a specific type of resource is also used to qualify the ResourceIdentfier objects. While the providers are usually not very big, they're still very central to how the workspace works.

How to work with the workspace

This section describes how you can work with the workspace. The first subsection shows how you can work with resources. The second subsection describes how a new resource type can be implemented, the third subsection describes how a new resource can be implemented.

Working with resources

First, you'll need a workspace:

Workspace workspace = new DefaultWorkspace( "/path/to/workspace" );

Then you'll need to initialize the resources:

workspace.initAll();

Now you're ready to go:

FeatureStore fs = workspace.getResource( NewFeatureStoreManager.class, "myfeature" ); ConnectionProvider prov = workspace.getResource( ConnectionProviderProvider.class, "postgresonsecundum" );

As you can see, the workspace doesn't explicitly want a ResourceIdentifier, but still requests the two identifier parts. Just give him the base provider class of the resource you'd like, and the actual ID.

How to implement a new resource (e.g. a new type of feature store or a new OGC web service)

To implement a new resource, it's advisable to browse through existing implementations of the same resource, e.g. if you want to implement a new feature store, check out the SimpleSQLFeatureStore implementation.

You typically need at least four new classes to implement a new resource:

  1. A ResourceProvider implementation: Invoked by the Workspace to create ResourceMetadata objects
  2. A ResourceMetadata implementation: Provides information on the required dependencies (depending on the configuration)
  3. A ResourceBuilder for the new Resource: Creates a Resource instances from a JAXB object (the XML configuration)
  4. The actual Resource class that implements the respective Resource subinterface (e.g. FeatureStore)

The first is the implementation of your ResourceProvider. This should be easy enough. When implementing #createFromLocation you'll notice that you'll need a ResourceMetadata implementation. There's really not much besides the metadata instantiation that usually needs to be done here.

The ResourceMetadata implementation should also not provide a lot of trouble. The only thing to keep in mind is that you'll need to figure out the dependencies of your resource during #prepare. In order to make life easy, just extend the AbstractResourceMetadata class, just add your dependencies to the dependencies field.

While implementing #prepare, you'll realize you also need a ResourceBuilder. At this stage, you'll probably still have the JAXB parsed configuration object. The idea is that you deconstruct the JAXB stuff and construct a proper Resource object during #build. Make sure you pass the ResourceMetadata object to the resource.

Last but not least, of course you'll need a Resource implementation. What that means exactly, is up to you of course. Just make sure the #getMetadata returns the proper metadata object (passed down during initialization), and you clean up after yourself in #destroy.

Don't forget to add a META-INF/services/my.resource.provider.package.MyResourceProvider with your provider fully qualified class name in it!

How to implement a new resource type (e.g. a new kind of data access)

To implement a new resource type, it's generally advisable to have a look at existing code. For a new resource type, you typically need two classes, a new ResourceManager and a new abstract ResourceProvider.

In order to make life simple, just extend the DefaultResourceManager. Add a default constructor, in which you call the super constructor like this:

public class MyResourceManager extends DefaultResourceManager<MyResource> {

  public MyResourceManager() {
        super( new DefaultResourceManagerMetadata<MyResource>( MyResourceProvider.class, "my resources",
                                                             "datasources/myresources" ) );
  }

}

Extend the AbstractResourceProvider to have the marker provider class:

public abstract class MyResourceProvider extends AbstractResourceProvider<MyResource> {
}

Don't forget to actually add your Resource interface:

public interface MyResource extends Resource {
  // add fancy methods
}

Then don't forget to add a META-INF/services/org.deegree.workspace.ResourceManager file containing your fully qualified ResourceManager class name.

That's all you need! If you have implementations of your resource provider on the class path, they'll get loaded automatically, and any resources your provider wants to handle will be initialzed automatically.

Clone this wiki locally