Jump to: navigation, search

Corona CC New Approach

This page is intended to describe and summarize a new approach, a better approach we believe, that could be used for CC and repository adapters. It doesn't provide in fact much changes to the model and repositories, but rather supplements current state.

It doesn't intend to keep all changes that should be done to CC. Proposals for CC changes is at a separate page. It more applies to API and semantic matters.

Some background

The main programming issue with repositories is repository adapter. The repository adapter should allow us to access repositories in some standardized way. For this purpose the repository adapter was introduced. But it had several weaknesses:

  • All arguments and return types are objects. This provides a problem that you cannot work with repository without knowledge of the repository. You always need to know what kind/type of the object is expected. This kind of interface cannot provide a generic API.
  • The repository adapter interface fits only to repositories that keeps entities of some kind. Even now not all repositories fit into this model!
  • Difficulties with reusing existing repository adapters by other repositories.
  • ...

Only one connection configuration active

Repository descriptor allows to have several connection configurations. The semantic of this wasn't well defined. The possibilities are:

  1. Alternative ways of connecting that software automatically picks one by its preferences or which connection it is able to handle.
  2. Alternative ways of connecting but one is default and user is able to select other if the default doesn't work for him.
  3. Merge information from various places into a single repository ... very difficult.

The first approach makes things very difficult, inconsistent and error prone. The second one, although gives a bit less power, is acceptably simple. From logic point of view, the repository definition is flat then, because there are no several possible ways of connection. But this means that repository descriptor object should have a method like "getConnectionConfig()", which would check user's preferences and if not available, it would take the default connection.

There is no longer a default connection configuration -- Glenn Everitt

When we initially designed the RepositoryConfiguration we assumed that one of the configuration would be the default and others would be for test configurations. We realized when we started deploying RepositoryAdapters on both Corona Clients and Servers that we needed to be able to choose the RepositoryConfiguration where it is deployed. We decided that the logic for deciding which RepositoryConfiguration could be moved into the RepositoryAdapterFactory. The RepositoryAdapterFactory could determine which RepositoryConfiguration should be instantiated. We also talked about have a component that would determine the RepositoryAdapter to use based upon the available adapters and the content-type, access-type and content-format. The logic to match a RepositoryAdapter implementation to a repository-descriptor could be put into the RepositoryAdapter factory. The logic could be similar to that used to match repositories to repository views in repositoryDescriptorConnectionProperties extension point implemented in ProjectContainerView.addRepositoryPages().

Parametrize IRepositoryAdapter

Good Idea -- Glenn Everitt

I think this makes sense and is worth doing - there was a suggestion about changing fetch resource to return something call IRepositoryResource. Does that suggestion still make sense given that we would use resource types through generics?

Well, I think the IRepositoryResource wouldn't be used. Of course it could one of the options of what th T might be. -- Marcin Okraszewski

Without breaking compatibility we can slightly modify IRepositoryAdapter and introduce ID and resource types through generics. So the IRepositoryAdapter would now look like this:

interface IRepositoryAdapter<I,T> {
    List<I> listIds(I parentId);
    boolean resourceExists(I id);
    T fetchResource(I id);
    I addResource(T res);
    void removeResource(I id);
    void updateResource(T);
}

Obviously, event though a given repository adapter implementation provides its types, it cannot be detected in runtime! So this doesn't give much, since we are using repository factory, which would loose the type declarations. To overcome it we can simply create a new repository interface extending the IRepositoryAdapter with definition of I and T types.

Some example. CVS, WebDAV, file system, Jackrabbit, FTP, etc. can be threated as a virtual file system. The key is some kind of path, value is a virtual file. So we should have a single interface IVfsRepositoryAdapter for those that we can use them interchangeable.

interface IVfsRepositoryAdapter<I extends IPath, T extends IFile> extends IRepositoryAdapter<I,T> {
}

This declaration creates a new interface but all I in IRepositoryAdapter must be of class IPath, while T of IFile. In addition, the IVfsRepositoryAdapter can be detected in runtime with instanceof. Note also that you can still build more specialized interfaces that extend IVfsRepositoryAdapter and put even strong constraint for the ID and resource types.

Advantages:

  • Provides type check for a repository adapter which reduces number of problems with class cast exception.
  • Allows to build API by providing specialized interfaces for given repository types. Repository adapters for different repositories may/should implement the same interface in order to have it easily replaceable.
  • The type check can be performed also at runtime by instanceof<code>
  • You can still work only at IRepositoryAdapter level with objects.
DO NOT IMPLEMENT IRepositoryAdapter DIRECTLY

For instance:

class FileSystemAdapter implements IRepositoryAdapter<IPath, IFile> {
  // ... implementation here ...
}

In compilation time the result is exactly the same as implementing IVfsRepositoryAdapter. The problem is that it cannot be detected in runtime.

Stackable repositories

There are some simple repositories, like CVS, where its connection definition is enough. But some repositories require access to other repositories. It is usually that something needs to get some file and parse it. For instance team xml file. It is kept either on disc, but can be kept in CVS or Jackrabbit repository as well. To abstract the real access to file, the team xml repository adapter requires some other repository adapter which would give it the file to parse.

So there are two levels - one is content adapter, second is access adapter. Content adapter uses access adapter to obtain information to parse. From repository user point of view, only content adapter is visible. But both content adapter and access adapter implement the same interface - IRepositoryAdapter. The access adapter is created based on "connection" element.

Here is a simple example of a team xml repository accessible through URL.

Stackable1.png

Reference between repositories

Instead of setting access information in connection configuration, we could point just to another repository descriptor. As the access interface is the same as for whole repository, this is completely transparent.

Here is an example of team member xml in CVS:

Stackable2.png

When factory is requested for an adapter for the connection object with reference, then it obtains an adapter for the referenced repository. The property resource points the resource that should be fetched in order to obtain the XML file with members.

This approach has one more advantage. For instance CVS can be access in anonymous or authorized mode. For each of them an other connection configuration can be made. Now, if several repositories refers to CVS repository, you just need to change active configuration once, to have an authorized or anonymous access.

The approach with repository reference should be rather preferred due to higher flexibility.

Multiple repository adapter interfaces for a single repository

Thinking of the repository adapters it turns out that very often a single repository may have more than one possible repository adapters. Taking CVS repository again. The CVS can have at least three repository adapters:

  • that operates on native CVS entries
  • that works as a "virtual file system"
  • that works on input streams

Usually when a module tries to access some repository it has some expectations to the type of resources. In other words it can give class of the interface that it expects to operate on. So the repository adapter factory should have and parameter with interface class, which should be returned.

Not only IRepositoryAdapter

Going further with this approach - who said that we should work only on repository adapters? The IRepositoryAdapter interface works well for any repository that keeps some artifacts (CVS, Jackrabbit, team members, etc.) but repository descriptors has already been used for web services, event router, etc. Those do not fit into IRepositoryAdapter. But when you need to specify interface you want to operate on any way, why should we restrict to IRepositoryAdapter? If you have a repository that keeps settings to event router, it is better to request an interface for event router. Those interface could look like this:

interface IEventRouter {
  void sentEvent(Event event);
  void registerEventHandler(IEventHandler handler);
  void unregisterEventHandler(IEventHandler handler);
}

And then if you want to send an event and have repository adapter, it is so simple:

IEventRouter router = RepositoryAdapterFactory.getAdapter(routerRepositoryDescriptor, IEventRouter.class);
router.sendEvent(event);

Look also how easy it is with web services then. We have already a module that is able to make a WS proxy in runtime for any interface. So, if the factory detects that you request for an adapter to WS endpoint, just makes a dynamic proxy for an interface given to factory!!! It makes it then completely transparent if you work on local repository adapter of just a proxy through WS.

I agree with the idea of integrating services other than repositories -- Glenn Everitt

I am not sure the definition should be as specific as an EventRouter, I was thinking about something more like ServiceReference for integrating web service endpoints. I was also thought ResourceReference for adding a single resource. For example, a Web Service Resource is really a stateful WebService which makes it like a web accessable resource object. I think we need to add the other concept of ContentAdapters to make this work. I think the ResourceReference would have to point to the ContentAdapter to use to convert the data into a useable object.

IAdaptable / IAdapterManager instead of RepositoryAdapterFactory

If our RepositoryDescriptor would implement IAdaptable, the conversion could be simply done with this interface. People who would use Corona API wouldn't need to know anything about some repository adapter factory. They would simply make this kind of code:

  IVfsRepositoryAdapter adapter = (IVfsRepositoryAdapter) repositoryDescriptor.getAdapter(IVfsRepositoryAdapter.class);

The factory functionality could be completely moved to IAdapterManager. This two would allow us to make completely Eclipse style solution.

Unfortunately I find one big problem with IAdapterManager. It need to register which classes you are able to change to which interfaces. The problem is that if we would like to have the factory for dynamic proxy for web services, we are not able to give a set of result interfaces. This factory would be able to adapt any interface!!!

So maybe the IAdapterManager is not the best idea? Or any know result interfaces would be done by IAdapterManager, but services would be done in other way ... I know, strange too :(

But definitely, the IAdaptable should be used. The question is only which factory for adaption should be used.