Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Corona CC New Approach"

(Stackable repositories)
Line 62: Line 62:
 
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.
 
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.
  
The interface for such adapter would be an extended version of IRepositoryAdapter to return resources of a given type. In order to have it implemented easily, there need to be a factory that obtains a IRepositoryAdapter for a connection configuration. The description of such repository in later section [[#IAdapterManager instead of RepositoryAdapterFactory]].
+
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 by a special factory for this. The factory subject is taken later.
  
 
=== Reference between repositories ===
 
=== Reference between repositories ===
Seems to be natural that some repositories may use other defined repositories. For instance team member xml file may be kept in an other repository, like CVS. So instead of setting everything in connection configuration, we could point just to an other repository descriptor. As the access interface is the same as for whole repository, this is completely transparent.
+
Instead of setting access information in connection configuration, we could point just to an other 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.
 
Here is an example of team member xml in CVS.

Revision as of 12:08, 8 March 2007

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.

Parametrize IRepositoryAdapter

Without breaking compatibility we can slightly modify IReposiotyAdapter 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 by a special factory for this. The factory subject is taken later.

Reference between repositories

Instead of setting access information in connection configuration, we could point just to an other 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.

<repository id="cvs1" content-type="files">
  <!-- conection params here -->
</repository>
<repository id="team1" content-type="team">
  <connection-configurations>
    <connection content-format="corona-team-xml" access-type="repo-ref" ref="cvs1">
      <property name="resource">team/members.xml</property>
    </connection>
  </connection-configurations>
</repository>

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.

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.

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.

Back to the top