Skip to content

BladeRunnerJS Internals

dchambers edited this page Sep 16, 2014 · 3 revisions

Domain Model

We have a set of domain objects that model the BladeRunnerJS concepts like blades, bladeset, aspects, etc. It looks something like this:

UML Image 1

and this:

UML Image 2

The domain objects can be used to both talk about things that currently exist on the file system, and talk about things that have yet to be created on the file system. This allows us to create new items on disk in a convenient way, for example:

brjs.app("myapp").bladeset("default").blade("blade1").create();

Domain Model File Synchronization

We have two utility classes (NodeList and NodeItem) that allow us to automatically create and delete domain objects as the physical file system changes. We use NodeList.list(), NodeList.item(String itemName) and NodeItem.item() within our domain APIs to return domain object(s) based on the current state of the file system.

The App class provides a good example of NodeList and NodeItem being used:

Memoization

We have a MemoizedValue class that allows us to safely memoize objects that are expensive to compute, without worrrying about introducing pernicious caching bugs.

Here's some information about its use:

  1. The Memoized.value() method is used to safely wrap access to memoized values.
  2. If the underlying object has changed the method used to generate a fresh copy is re-invoked.
  3. We can be fairly sure there are no caching bugs since when we run our tests we ensure that each MemoizedValue class correctly declares the files, which if they changed, should cause us to create a fresh copy of the object.
  4. We do this by installing a Java SecurityManager when running tests (called BRJSSecurityManager) that is informed of all file-access, and which ensures that any file access within MemoizedValue.value() invocations has been pre-registered.

The PersistentAliasesData class provides an example of it's use:

File Watching

The MemoizedValue class makes use of the BRJS.getFileInfo() method, which in turn makes use of an underlying FileModificationService instance which is passed into the BRJS constructor at initialization. We have three implementations of FileModificationService:

  • Java7FileModificationService (used in the real app)
  • PessimisticFileModificationService (used within the majority of tests where we want changes to the file system to be picked up immediately)
  • OptimisticFileModificationService (used within a few tests where we don't want file system changes to be picked up)

The FileModificationService interface, and corresponding FileModificationInfo interface, are here:

Bundling

The BundlableNode.getBundleSet() method is used to create a BundleSet instance of all the assets that could potentially be sent to the client. Aspect, Workbench and TestPack all implement BundlableNode, and can have bundle-sets created for them.

The actual assets that are sent to the client depend on the tag-handlers a developer uses in their index page, and whether classes like HtmlResourceService and XmlResourceService are used, since these make XHR requests to content plug-ins within the server to fetch more data to the client.

You can follow the entire bundling process by inspecting the BundleSetCreator and BundleSetBuilder classes here:

Plug-ins

What happens when the BundlableNode.getBundleSet() method is invoked depends on the set of plug-ins available, since these affect what assets will be found on disk, and how they will be processed. More information on plug-ins is available here:

Trie

A number of plug-ins make use of the Trie and TrieFactory utilities, that allow implicit dependencies (dependencies that are not explicitly defined using require(), etc) to be efficiently found within file content those plug-ins scan. The Trie class itself is generic, and does the real work, whereas the TrieFactory is used to share Trie instances that are common to an AssetContainer.

Whereas a map (e.g. Map<String, Book>) allows books to be efficiently retrieved using a key of type String, the trie (e.g. Trie<Book>) allows references to books to be efficiently discovered while scanning arbitrary text content.

More information on tries can be found here:

and the Trie and TrieFactory code is available here:

Clone this wiki locally