Data Accessor Guide
System Development Guides
Release Notes
Developing with NetKernel
Module Development Guide
Active Accessor Guide
Data Accessor Guide
Request Guide
Universal Resource Infrastructure Design
Universal Resource Request Design
Representational Aspects
Transreptor Development Guide
Session Guide
Mod DB Guide
Compound URI Guide
License
Change History
NetKernel History
Acknowledgements

URA Development Guide - Data Accessors

Details on developing Data Accessors

Registered Data Accessors enable ranges of URIs to be accessible to applications running with the NetKernel system. The kernel comes pre-configured with some standard Data Accessors for accessing file:, http:, ftp:, etc.

The Interface

All accessors must implement the interface com.ten60.netkernel.urii.IURAccessor. However a baseclass org.ten60.netkernel.layer1.accessor.AccessorImpl is defined in module layer1 which provides a handy default implementation for most instances.

Request Types

Data accessors can do more that just source representations of resources. This section describes all the request types that accessors can handle. Request types are declared as constants in the class com.ten60.netkernel.urrequest.URRequest.

  • RQT_SOURCE A request to return a representation of a resource.
  • RQT_SINK A request to update the resource with a given representation.
  • RQT_EXISTS A request to check for the existence of a resource for a given URI.
  • RQT_DELETE A request to delete a resource for a given URI.
  • RQT_NEW A request to create a new for a representation and return its assigned URI.
For more details of the content and structure of the different types of request see the Request Guide.

Lifecycle

Accessors are constructed on demand when the first request that needs them is issued. Accessors are constructed using the default constructor but shortly afterwards the void init(URIdentifier aURI, ModuleDefinition aModule, Container aContainer) method is called that provides the accessor with its assigned URI and the module and container it is hosted in. Once created the accessor may be cached so that subsequent requests can reuse it. If it isn't it will be used for one request only.

After creation an accessor will be used to process a request. The kernel will make a call to void receiveAsyncRequest(URRequest aRequest) with the request to be processed. After the accessor has finished servicing a request and when the cache no longer wants to keep it the accessor will be garbage collected like a regular java object, the void finalize() method is called. This should be used to perform any cleanup that may be required.

Metadata

During construction the accessor must create its metadata, this implements com.ten60.netkernel.urii.IURAccessorMeta. An implementation is provided in layer1 module org.ten60.netkernel.layer1.meta.DataAccessorMeta. The important values in the metadata are:

  • isThreadSafe true if multiple concurrent requests can be processed by the accessor.
  • creationCost a cost value for creation of the accessor itself.
  • supportsRequestType(type) true if the accessor supports the request type.

Issuing Sub-requests

Usually a data accessor is a primary source of a resource and it does whatever lower-level work to perform an action on a resource defined by a URI. However there are a few circumstances where an accessor might wish to make further sub-requests before completing:

  • reading configuration resource
  • performing some transformation on the raw resource
For more information on making requests see the Request Guide.

Example

As an example we will develop an accessor to implement an in-memory document storage accessor using a transient: URI scheme. The accessor will allow storage and retrieval of documents under arbitrary keys, i.e.

  • transient:myResult
  • transient:/sessions/1/data
The actual path value in the URI isn't important it is just a unique identifier.

Step 1 - declare class

Subclass org.ten60.netkernel.layer1.AccessorImpl and provide a default constructor.


    package org.myDomain.myApp;
    
    import org.ten60.netkernel.layer1.accessor.AccessorImpl;
    import org.ten60.netkernel.layer1.meta.DataAccessorMeta;
    import com.ten60.netkernel.urrequest.URRequest;
    import java.util.*;
    
    class TransientDataAccessor extends AccessorImpl
    {   private static final int COST = 2;
        private static final int REQUEST_TYPES = URRequest.RQT_SOURCE | URRequest.RQT_SINK;
        public TransientDataAccessor()
        {   super( new DataAccessorMeta(COST,REQUEST_TYPES,THREADSAFE) );
        }
        
        private final Map mDocuments = new HashMap();
    }

Step 2 - implement functionality

The source method looks for any existing data in the HashMap but will return back an empty result for paths it doesn't know about. Generally there seems to be two ways of handling a URI that doesn't exist. You can either return back a default of throw an exception. Returning a default is easier as you must provide an alternate mechanism for creating new URIs if necessary, this might involve building an active accessor to provide the creation functionality.

The sink method simply serialises the document and stores it in the HashMap under the key of the URI path.


    public void receiveAsyncRequest(URRequest aRequest)
    {   try
        {   URRepresentation result = null;
            switch (aRequest.getType())
            {   case URRequest.RQT_SOURCE:
                    result = source(aRequest);
                    break;
                case URRequest.RQT_SINK:
                    result = sink(aRequest);
                    break;
            }
            URResult urr=new URResult(aRequest, result);
            getScheduler().receiveAsyncResult(urr);
        }
        catch (Exception e)
        {   NetKernelException nke=new NetKernelException("Error Processing "+aRequest.toString());
            nke.addCause(e);
            URRepresentation result=NetKernelExceptionAspect.create(nke);
            URResult urr=new URResult(aRequest, result);
            getScheduler().receiveAsyncException(urr);      
        }
    }
    
    private URRepresentation source(URRequest aRequest) throws Exception
    {
        URRepresentation result = null;
        String path = aRequest.getURI().getSchemeSpecificPart();
        String data;
        if (path!=null)
        {   String data = mDocuments.get(path);
            if (path==null)
            {   data="";
            }
            IURMeta meta = new AlwaysExpiredMeta(cost,mime);
            return StringAspect.create(meta,data);
        }
        else
        {   throw new Exception("URI must have path element");
        }
    }
    
    private URRepresentation sink(URRequest aRequest) throws Exception
    {   URRepresentation data = aRequest.getArg(URRequest.URI_SYSTEM);
        if (!data.hasAspect(IStringAspect.class))
        {   data = transrepresent(aRequest.getURI(),data,aRequest);
        }
        IStringAspect sa = (IStringAspect)data.getAspect(IStringAspect.class);
        String path = aRequest.getURI().getSchemeSpecificPart();
        mDocuments.put(path,sa.getString());
        return VoidAspect.create();
    }

Step 3 - register the accessor

This step maps a range of URIs onto the accessor we have just created.
Within the module.xml add a mapping entry:

<ura>
  <match>transient:.*</match>
  <value>org.myDomain.myApp.TransientDataAccessor</value>
</ura>
This example maps all URIs for the transient: scheme to use our new accessor. For more details see Active Accessor Guide.


1060® NetKernelTM Documentation
(C) 2003-2004 1060 Research Limited

Send Feedback

© 2003,2004, 1060® Research Limited
1060 registered trademark, NetKernel trademark of 1060 Research Limited