Giter Site home page Giter Site logo

jactor2's Introduction

jactor2's People

Contributors

dallaybatta avatar laforge49 avatar skunkiferous avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jactor2's Issues

Drop agents

I realized this morning that there is nothing you can do with agents that can not be done more easily and efficiently with a request. Conclusion: I am still a novice when it comes to JActor2! :D

The only exception would be an agent that retains state between invocations. But that seems to be pretty rare. And more likely we would just use a blade that supports multiple requests anyway.

So we should drop agents. And replace PrinterAgent with an abstract class, SyncPrinterRequest.

static convenience methods for Printer

Two static convenience methods on Printer would be nice:

public static SyncRequest<Void> printlnSReq(final Facility _facility, final String test) throws Exception;

and

public static SyncRequest<Void> printfSReq( final Facility _facility, final String _format, final Object... _args) throws Exception;

QueryState

A generic class which wraps some collection object, providing read-only access and which can be disconnected from that object.

Facility interdependencies

Need requests to add a dependency.

When adding a dependency on another facility to a facility, the name of the facility being added as a dependency is used to set a property named "dependency_"+name.

Also, when adding a dependency, addAutoClosableSReg is applied to the dependency being added as a dependency.

But a dependency can not be added if already present in the properties of the newly dependent facility.

Implement systematic failure simulation, to harden the API

MIGRATED FROM PAMailbox

To harden the API, as much as is possible, we could add assert statements that call some "test-class", built to throw random exceptions, in a configured way. The idea is that with the assertion disabled, as is expected for production code, this would be totally eliminated at runtime, and cost no resources. But when activated, would cause "random exceptions" to be thrown, in most possible places, allowing us to truly test the exception handling, and check that no request, response or exception gets totally lost. Failure is always possible, but it should be documented, and the goal is that the end-user knows what to expect, with regards to what can go wrong where, and what will happen when it goes wrong.

Printer as a singleton

Currently we allow each facility to have a stdout, which is not correct.

Update stdoutSReq so that, if a facility has a dependency on a plant facility then the stdout property of the plant facility is used.

Property change validation

Prior to effecting a property change, it needs to be validated.

This is an especially interesting requirement because we need something like an event bus, but we can't use events because we need a response.

public Interface PropertyChangeValidator {
//The request returns a null if the property is valid, otherwise it returns an error message.
AsyncRequest<String> validatePropertyChangeAReq(String _propertyName, Object _oldValue, Object newValue);
}

Facility will need to support methods addPropertyChangeValidatorSReq and removePropertyChangeValidatorSReq.

In addition, Facility should validate the property changes of interest to itself.

Plant as a singleton

Lets add a new private static to Plant, singleton, and throw an exception in the constructor if a Plant instance has been created. Also add a getSingleton method. Note that no changes are needed to the API.

I was previously avoiding the use of a static variable here, but I've been thinking about it and see little downside.

Create a sample/misc "pool implementation"

In most applications that have "long processing" (say, batch image format conversion) or do network/IO calls, there is a need to limit the number of request being processed in parallel. While having a single reactor do all the work is a solution, sometimes you want to devote more then one thread to it.

A nice solution to this is a "pool". You have a "pool manager" (non-blocking) reactor (blade?) that receives all requests to the pool. This reactor in turn manages a "pool" of Isolation reactors, that do the actual work. It's job is to pass the requests to the pool nodes, such that the incoming requests are processed in order, and as fast as possible, while hiding the implementation of the pool itself. As in typical thread-pools, one has the choice of fixed-size, or grow-as-you-need (un)bound pools, but this should not normally be visible to the pool user. So we might want to offer more then one implementation of the "pattern".

Adding "high priority" Flag to Requests/Events

In event/message based systems, some things are more "important" then others. While a full-blown request priorisation subsystem might be an overkill atm, having a simple "high priority"/out-of-band flag for Requests/Events would get us a long way.

This would simply cause the new event/message to be set as "next in the list", instead of as last. Possibly it needs not be a flag in the event/message, but rather a send/call optional parameter.

Drop events

Events are no longer needed. In issue #37 we learned that requests can be used even to implement an event bus.

rename

Message processor -> Reactor
Actor -> blade

UpdateState

A generic class that provides read/write access to some collection, which can be disconnected from that collection, and which tracks the changes to that collection for validation prior to application of those changes.

Closeable Reactors

Atm, reactors are "kind of" closeable. They are put in the "auto-closeable" list of the Facility. But this is done in the base classes, not over the interface.

I think the changes required are mostly:

  1. Add a close() method to the Reactor interface
  2. Make sure a closed Reactor removes itself from the Facility "auto-closeable" list
  3. Drop Events and "fail" Requests sent to a closed Reactor.

no signals/events on property change notifications

When a signal is used for property change notifications, race conditions are a distinct possibility.

So we an not use event bus for this. Indeed, we drop the use of event bus entirely.

Note that this requires putProperty to be async, so issue #37 needs to be completed first.

UpdateRequest

An async request whose factory method takes one argument--an UpdateState.

Initiator, aka OSGi Activator

public interface Initiator extends Blade {
void initialize(final Reactor _reactor) throws Exception;
AsyncRequest startAReq();
}

Add the following method to Facility:

 public ASyncRequest&lt;Void&gt; initiateAReq(String initiatorClassName) {
     ...
 }

The initiate request will load the initiator class and invoke its start request. This works in conjunction with issue #28. Modules then must have a well-known Initiator subclass.

The reactor of an initiator will be the facility's own internal reactor.

Simplified request composition

Currently, if you want to dynamically add (at run time) a request to a blade, you must create a second blade that contains the new request. For example:

static public AsyncRequest&lt;Printer&gt; stdoutAReq(final Facility _facility) throws Exception {
    return new FacilityAgent&lt;Printer&gt;(_facility) {
        protected void start(final AsyncResponseProcessor<Printer> dis) throws Exception {
            Printer printer = (Printer) getProperty("stdout");
            if (printer == null) {
                printer = new Printer(new IsolationReactor(_facility));
                putProperty("stdout", printer);
            }
            dis.processAsyncResponse(printer);
        }
    }.startAReq();
}

So when, for example, calling stdoutAReq(Facility), first you create an anonymous subclass of FacilityAgent and then create the start request. Two objects where really only one is needed.

Instead, we can just create the new request directly:

static public AsyncRequest&lt;Printer&gt; stdoutAReq(final Facility _facility) throws Exception {
    return new AsyncRequest&lt;Printer&gt;(_facility.getReactor()) {
        public void processAsyncRequest() throws Exception {
            Printer printer = (Printer) _facility.getProperty("stdout");
            if (printer == null) {
                printer = new Printer(new IsolationReactor(_facility));
                _facility.putProperty("stdout", printer);
            }
            processAsyncResponse(printer);
        }
    };
}

Now we are only creating a single object. To get this to work cleanly, we should add two methods to RequestBase:

/**
 * Process the request immediately.
 *
 * @param _syncRequest    The request to be processed.
 * @param &lt;RT&gt; The type of value returned.
 * @return The response from the request.
 */
protected &lt;RT&gt; RT local(final SyncRequest&lt;RT&gt; _syncRequest)
        throws Exception {
    return SyncRequest.doLocal(targetReactor, _syncRequest);
}

/**
 * Process the request immediately.
 *
 * @param _request        The request to be processed.
 * @param &lt;RT&gt; The type of value returned.
 */
protected &lt;RT&gt; void send(final RequestBase&lt;RT&gt; _request,
                                    final AsyncResponseProcessor&lt;RT&gt; _responseProcessor)
        throws Exception {
    RequestBase.doSend(targetReactor, _request, _responseProcessor);
}

UpdateFactory

An interface which contains an UpdateRequest factory method.

Use fast 1-to-1 queues to implement "ports"

MIGRATED FROM PAMailbox

Following the discovery of the much faster 1-to-1 non-blocking queues, we are now looking how to put those to good use, to improve performance, but without compromising on the current API. Bill suggest we had a new method in Mailbox (not MailboxFactory) that creates a "port" for "direct messaging" from one specific MB to another specific MB. This would allow the actors to have "normal" Mailboxes, but at the same time achieve much faster exchange between specific MB that have a lot of exchanges to do.

How to go about implementing this is not clear yet (at lest not clear to me).

Bill:
I think it is especially important that methods that direct a message to be passed via a 1-1 queue be separate from the application logic. Tuning a program then can be easily done after the program is working.

Facility.plantSReq

Only one facility should be named plant.

plantSReq returns the facility named "Plant" present as a dependency in a facility's property set.

Light-weight Modularity: Facility ClassLoader

A Facility may optionally have one or more JAR files associated with it.

The class loader returned by Facility.classloaderAReq will provide access to classes of the class loaders of its dependencies, as well as the classes of its associated jar files.

The class loader will be created the first time classloaderAReq is accessed, after which there may be no additional dependencies and no additional jar files.

Use weak-references for Reactors in the Facility auto-closable list

Atm, AFAIK, new Reactors are added to the auto-closeable resource list of the Facility, so they are closed when the facility is closed. But they are hard-referenced, and not explicitly closeable. This means that they never get garbage-collected until the Facility closes. This is a memory-leak. One solution is to use a weak-reference to the reactors in that list, so they can be automatically garbage-collected when no-more referenced. The same could apply to other resources in the list.

IMHO, the user should choose, for each resource, including the reactors, if they should be weakly, or hard, referenced if the auto-closeable list. This is because some resources holding references to system resources (memory-mapped IO channels, ...) really do need to be explicitly closed, and not simply garbage-collected.

The OSGi project needs some love

AFAIK, some tests in the OSGi project are failing, and therefore the tests have been turned off. We need to fix the tests, and re-enable them.

jactor.debug system property

  1. Move DEBUG from Facility to Plant.
  2. Initialize DEBUG using the jactor.debug system property. But keep it static final.

Facility property notifications

Define a property change notification event, and allow blades to subscribe/unsubscribe.

A successful subscribe (not already a subscriber) request returns a copy of the properties.

QueryRequest

An async request whose factory method takes one argument: a QueryState.

We need some kind of CRON-like API

MIGRATED FROM PAMailbox

The discussion about timers reminded me of java.util.Timer. I think something like that is essential to an Actor API. Maybe we can extract it from Project Harmony, and turn it into one of our API classes?

Bill:

Easy enough to write an actor to do it. I like to think of PAMailbox as a minimalist kernel, feature poor, fast and stable. CRON is a service, a part of an OS, but not a part of the kernel.

I think what we really need is a singleton actor which holds a set of Named (see PAUtil for Named) actors that provide various services. A CRON actor would be a first such service. All we really need is for MailboxFactory to hold that singleton, via an effectively final (set once) property. That's all the hook you need.

Skunkiferous:

Fair enough. But before I jump into implementing this, we need to work-out how those "Services" relate to OSGi Services (if at all).

Bill:

not at all. they are just actors associated with an overarching service. hmm. lets wait on this, I'm realizing that there may be different sets of actor services associated with different OSGI services. And I have a lot to do before diving into OSGI again.

CRON should be built over a database, I am thinking. This would be a lovely application for jfile, which I will be the next thing I migrate to the PActor API.

Allow "static" access to the current Reactor

Sometimes, it happens that deep in user code, you need to get access to the context within which that code is executed, without having to pass that context all the way down from the top of the stack. This is usually done in Java using Thread-local instances. Since Reactors are our "lightweight threads", it makes most sense to give a static access to them. So we would be tracking the reactor in the PoolThread, to allow this to be implemented (set current reactor in thread, when switching of reactor, such that we can then get the current thread, cast it to PoolThread, and ask for the reactor).

Drop putProperty

Drop the already deprecated Facility.putProperty.

This also means dropping the osgi module, as the test for OSGi no longer runs and is currently unsupported. But 0.4.3 does provide an alternative to OSGi, at least for some of the simpler use cases like plugin support.

This should be the last issue implemented in milestone 0.4.3.

Make PoolThread an interface

org.agilewiki.jactor2.core.facilities.PoolThread is currently a subclass of Thread. To allow more freedom to customize the thread-pool, it would be better if PoolThread was an interface.

Optional message processing timer

MIGRATED FROM PAMailbox

An optional timer on non-blocking mailboxes to ensure that all messages are processed in less than 1 millisecond.

The timer is there to return a "timeout exception" as response if the requests takes too long. It has no effect on the processing of the request itself. This does not in effect actually deal with issues like infinite loop, as the loop just keeps on going, even if the caller gets a response.

Bill:

The idea then is to limit the amount of time the requests of a non-blocking mailbox can run, not to limit the amount of time it takes to complete a request. Remember that requests are non-atomic, but message processing is atomic.

Indeed, this does not prevent loops which include a request/response, but it does address the commonest bug that I have experienced, which is forgetting to call ResponseProcessor.processResponse--which currently results in a mysterious hung program. Adding this check will make it much easier to locate this kind of bug and consequently it will be much easier to learn how to use PActor.

This will cause things to slow down a bit, so we need to either be able to disable it or implement it in a debug version of PAMailbox, or both.

When a timeout does occur, all we need to do (methinks!) is to create a timeout exception and return it as the result for the mailbox's current message.

QueryFactory

An interface which has a QueryRequest factory method.

Facility's internal reactor need to be a transactional reactor

With the conversion of putProperty and change notifications to async and the need for async property validation, there are just too many ways for things to go wrong. And it turns out, an IsolationReadtor can not handle things like creating a resource if not present. We need a transactional reactor that supports queries and validation/update.

IsolationReactor should be deprecated.

Plant required

Every Facility must have or be a Plant. I.E. Time to drop the public constructors from Facility.

The Plant maintains a map of all facilities. And when a facility is closed, it is removed from this map.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.