Skip to content
Thomas Schwotzer edited this page Mar 21, 2023 · 28 revisions

TODO - this section will be re-written

Setup

Before you anything else: Add two additional libraries to your project: SharkPeer and ASAPHub.

This is out of the way now. Let's talk about our component model.

Table of content

Your apps would most probably be helpful in other applications. We need components. We need a way to put them together. That is what we call Shark – Shared Knowledge. It is a concept: Implement decentralized apps (based on ASAP) and make them work together.

What is to be coordinated?

It is not even difficult. Each ASAP application uses an ASAPPeer to communicate with others. Applications can be distinguished by a format. Obviously:

  1. Different applications must not use the same format. ASAP itself uses some formats for internal management purposes. They must not be used either.

Applications might have dependencies. There is for instance an ASAP application that manages certificates. It is strongly recommended to use it instead implementing your own. This application must be initialized before an application that uses it.

  1. Applications must be initialized / created in a well-defined order. There must be a way to get a reference to applications we want to use in our own.

Those applications will share a single ASAPPeer. It must know all formats within that larger application before it is started. There is no way around:

  1. Setting up such an united applications is a two step process: First, all components have to be initialized and have to announce their formats. Second, the ASAPPeer can be started.

Shark Component

Your ASAP application has to become a SharkComponent to become a part of such a united ASAP Application – a Shark Application.

Here is an example:

@ASAPFormats(formats = {YourComponent.FORMAT_A, YourComponent.FORMAT_B})
public class YourComponent implements SharkComponent, YourFacade {
    public static final String FORMAT_A = "myApp://formatA";
    public static final String FORMAT_B = "myApp://formatB";
    ...
}

There must be a single interface (or class) that describes your ASAP application (YourComponent). It must implement SharkComponent but also offer all methods of your application (symbolized by YourFacade). The list of your interfaces could be longer. In general, it is good idea to follow the Facade Pattern when sharing a software component, though.

The @ASAPFormats annotation is mandatory. All supported formats must be declared with the formats parameter. That's it. You have successfully declared your ASAP application as Shark Component. Other example(s): CertificateExchange

That is actually a bad example. We strongly suggest to use a single format for each application. Nevertheless, in rare cases, more than one is necessary. Just one format is better, though.

Shark Component Factory

Who could better describe the creation of your application then you? You do not want to explain others how to do this? Even if you would: Would you like to read lengthy explanations of others? I do not think so.

Fortunately, there is design pattern for that: Factory pattern. You provide code that sets up your applications. That code is in a factory object.

public class YourComponentFactory implements SharkComponentFactory {
    @Override
    public SharkComponent getComponent() {
        YourComponent yourComponentInstance = …;
       // do your initialization / creation
        return yourComponentInstance;
    }
}

Setup Shark Peer

Finally, we need to explain how to set up a united application. The SharkPeer represents this unification. It represents the collection of more than one ASAP application. There is one implementation of this interface now: SharkPeerFS.

static final CharSequence ALICE = "Alice";
static final CharSequence ROOTFOLDER = "sharkComponent";
static final CharSequence ALICE_ROOTFOLDER = ROOTFOLDER + "/" + ALICE;
...
SharkPeerFS sharkPeer = new SharkPeerFS(ALICE, ALICE_ROOTFOLDER);

// add another application
AnotherComponentFactory factory = new AnotherComponentFactory();
sharkPeer.addComponent(factory, AnotherComponent.class);

// add your application
YourComponentFactory factory = new YourComponentFactory();
sharkPeer.addComponent(factory, YourComponent.class);

// add an additional one with dependencies
AnotherComponent requiredComponent = sharkPeer.getComponent(AnotherComponent.class);
DependendComponentFactory factory = new DependendComponentFactory(requiredComponent);
sharkPeer.addComponent(factory, YourComponent.class);

// launch Shark peer
sharkPeer.start();

SharkPeerFS works with ASAPPeerFS. It requires a peer name and folder name to keep data. Components can be added. Users of your application should only need to read and understand your YourFacade and must be able to create a factory object of yours (YourComponentFactory).

  1. Note: Your application cannot choose peer name and rootfolder by itself any longer. Those parameters are defined with SharkPeer creation. If you need them: There are two ways to get them.

Here is the first way – not the preferred one, though: Define you factory constructor in that way, like

public class YourComponentFactory implements SharkComponentFactory {
    YourComponentFactory(CharSequence peerName, CharSequence folder) {
        // keep it
    }
     ...
}
  1. Note: Components which are required from others could and should be parameter of the factory as illustrated with DependendComponentFactory.

  2. Note: Components can only be added and removed until the Shark peer is started

Shark Peer startup

The startup process is very simple: An ASAPPeer is created with the collection of all supported formats. Each component is informed about this action: The void onStart(ASAPPeer peer) is called. Your component, your ASAP application has got access to the ASAPPeer object and can act as usual. This is the second and preferred way to get access to peer parameters like its name.

The following code is from the ASAP Certificate project

@ASAPFormats(formats = {ASAPCertificateStore.CREDENTIAL_APP_NAME, ASAPCertificateStorage.CERTIFICATE_APP_NAME})
public class SharkCertificateComponent implements SharkComponent, ASAPKeyStore, ASAPCertificateStore {
    private FullAsapPKIStorage asapPKIStorage = null;

    @Override
    public void onStart(ASAPPeer asapPeer) throws SharkException {
        try {
            ASAPStorage asapStorage = asapPeer.getASAPStorage(asapPeer.getPeerName());
            ASAPCertificateStorage asapAliceCertificateStorage =
                new ASAPAbstractCertificateStore(asapStorage, asapPeer.getPeerName(), asapPeer.getPeerName());
            InMemoASAPKeyStore inMemoASAPKeyStore = new InMemoASAPKeyStore(asapPeer.getPeerName());
            this.asapPKIStorage = new FullAsapPKIStorage(asapAliceCertificateStorage, inMemoASAPKeyStore);
        } catch (IOException | ASAPException e) {
            throw new SharkException(e);
        }
    }
    …

    private void checkStatus() throws SharkStatusException {
        if(this.asapPKIStorage == null) {
            throw new SharkStatusException("ASAP peer not started component not yet initialized");
        }
    }

    // just an example of a interface implementation
    @Override
    public PublicKey getPublicKey(CharSequence charSequence) throws ASAPSecurityException {
        this.checkStatus(); // it would produce an exception if called before the Shark Peer was started
        return this.asapPKIStorage.getPublicKey(charSequence); // delegate call
    }
    ...
}

Details of the algorithm in the onStart method are not relevant but one fact is:

The actual creation of this component is made in the onStart method and not in the factory method. Calling any method before the SharkPeer was started would produce a SharkStatusException , see line this.checkStatus(); in getPublicKey. This pattern separates the creation of the component object from setting up its functions. This class also follows the Delegation pattern.

Here is the factory implementation:

public class SharkCertificateComponentFactory implements SharkComponentFactory {
    private SharkCertificateComponent instance = null;
    @Override
    public SharkComponent getComponent() {
        if(this.instance == null) {
            this.instance = new SharkCertificateComponent();
        }
        return this.instance;
    }
}

An object is created but nothing is initialized. Initialization requires an ASAPPeer and is made in void onStart(ASAPPeer) instead.

This library add some functions to ASAPPeer. Most importantly:

Introduction

Shark Components

  1. Describe your component
  2. Component initialization
  3. Setup Shark App
  4. Component launch

ASAP Hub Management

  1. What's a hub
  2. hub information management
  3. connection management
Clone this wiki locally