-
Notifications
You must be signed in to change notification settings - Fork 73
Code Structure
This document explains the code structure as of August 2015.
Lighthouse is written in a mix of several languages:
- Java 8
- Kotlin
- FXML (XML based layout)
- JavaFX flavoured CSS
If you've done web development then you already know most of the CSS dialect, it's a subset of CSS2 with some differences so most properties have a vendor -fx- prefix. FXML is quite straightforward but the best way to work with it is to use Scene Builder, distributed by Gluon.
Java 8 is basically regular Java with lambda functions/closures, using the syntaxes:
(a, b) -> { use(a); print(b); }
() -> callFoo()
list.forEach(MyClass::doFoo)
Most languages have some form of syntax like this.
Kotlin is a new language that you certainly will not already know. It can be thought of as what Java would look like if it was designed today and not in the early 1990s. It is conceptually the same and does not involve any massive changes to how you write code, but it dramatically simplifies Java, removes boilerplate, increases safety and improves expressiveness. You can learn it by simply reading the Kotlin website, or working through the examples at http://try.kotlinlang.org. Kotlin is highly readable so even if you don't know it, the Lighthouse code should be easily understandable.
The code is being steadily converted to Kotlin from Java. Eventually the whole app will use Kotlin.
Kotlin files end in .kt but can appear in the src/main/java directory (for convenience reasons). A single Kotlin file can define many classes or top level functions.
There are three top level Maven modules:
- client
- server
- common
They contain code and their purpose should be self explanatory. If you don't understand the client/server architecture, please review the design doc.
There are also extra directories:
- i18n: contains translation related files and scripts, plus the translated strings
- doc: contains design docs
- package: contains misc control files for native packaging
The code on the git master branch creates an unbranded program that calls itself Crowdfunding App. On the lighthouse git branch are a set of patches that add the (proprietary) Lighthouse branding and some scripts to build native installers for each platform. It also adds the "adverts" you can see on the overview screen. Develop on the master branch and don't worry about the lighthouse branch.
The code has a straightforward frontend/backend architecture. The LighthouseBackend class is in the common module and is used by both the server and client. The backend class handles the following tasks:
- Loading project and pledge files from disk, allowing lookup.
- Maintaining observable data structures indicating the state of projects and pledges.
- Downloading project status against the server and updating the data structures.
- Checking pledge status (OK, revoked) from the peer to peer network.
- Monitoring the Bitcoin network (using bitcoinj) to spot claims and revocations, then updating the status data in response.
The backend has a single thread that it uses for the bulk of its own work. Its data structures are only ever updated from this thread. Code that uses the backend and wishes to be informed of change of state use mirrored data structures. These are observable lists, maps etc that are associated with a particular thread. When the backend modifies its own data structures, a closure is posted to the associated thread that updates it. This means the backend can e.g. change the state of a project whilst the UI is in the middle of drawing itself: the copy of the data the UI is working with won't change until it returns to its event loop.
The backend runs on a separate thread from the UI in order to avoid animation and responsiveness glitches, and thus to enable a simple blocking programming style in backend code.
The server also uses the backend, but in this case, all HTTP request handling and backend work are run on the same thread. This avoids the need to duplicate the data structures in memory.
In the client app (but not the server at the moment), there is a BitcoinBackend class which manages the bitcoinj classes and wires them up. It duplicates the functionality of the bitcoinj WalletAppKit API but simplifies some things which is helpful to improve startup time.
The client starts in Main. It brings up MainWindow which is the primary UI controller class, and uses the layout defined in main.fxml.
MainWindow defines the white bar at the top of the screen that contains wallet related controls, such as the balance label, the buttons to configure the wallet/empty it, the address etc. It also defines the back button, and the main area where activities are placed. An activity is something that takes up the main area.
The MainWindow controller owns a NavManager, which is responsible for managing a stack of activities. In future it will handle animated transitions, etc.
Currently there are only two activities: the overview and the project views. In future there will be more.
Main and MainWindow together also do misc other tasks:
- Main handles blurred in popup windows, such as when you pledge.
- Main kicks off the first-run online update check and loads user prefs.
- Main contains various global variables giving access to app state from around different modules.
- MainWindow handles showing progress for online update checks done after first run.
- MainWindow hooks up Bitcoin wallet state from the bitcoinj objects to the UI, via the
BitcoinUIModelclass.
There are two key classes here: Project and LHProtos.Pledge. Both projects and pledges are represented on disk and network using protocol buffers. The protobuf library generates Java classes to work with these structs, and for pledges, we simply use that class directly. For a project, we use a wrapper class that adds various functionalities on top.
Both Project and LHProtos.Pledgeare immutable. When the user is editing a project, we use instead a class calledProjectModelwhich is a mutable equivalent that eventually, when the user is done, produces a regularProject`. Projects are often referred to by the hash of their contents, this is the project ID.