Under the hood¶
This chapter will give you a brief overview about the technical aspects of the Docker2go app. The code snippets should help you to understand the concepts.
Layout¶
The layout is implemented as a card layout and most of the time leverages a ListView
to display entries.
The ListView in ConnectionActivity
is served from a local SQLite Database accessed by SugarORM.
On connecting, a new Activity gets displayed which is based on a TabbedActvity
.
The three Tabs (Information, Containers, Images) are implemented as Fragments
.
In those fragments are different layouts implemented.
But they all share the SwipeToRefresh
paradigm which should help to make Docker2go more usable.
Additionally, on the containers Tab (TabContainers
) a BottomSheet gets displayed by clicking on a ListViewItem.
This BottomSheet gives you some basic actions based on the containers state and shows more details information.
For example the Docker Image is displayed in the BottomSheet whereas its not visible in the ListView.
The activity parent (OverviewActivity
) of the Fragments holds the Connection object in order to call the remote Docker API.
Multithreading¶
Every netowork based operation is done concurrently inside a AsyncTask
in order to not freeze the userinterface on long lasting operations.
The Connection
class provides the method executeCommand()
which lets you execute one or more command on a remote host.
For executing command on remote hosts the AsyncTaskCommandExecutor
class is used.
Every call to executeCommand()
is encapsulated in a single JSch connection.
@Override protected void onPreExecute() { // establish a secure connection to remote host }
This class is able to execute one or more commands which must implement the Command
interface.
@Override protected CommandExecutionSummary doInBackground(Command... commands) { try { // connect to remote host this.jschSession.connect(); // Execute single command and use onProgressUpdate for output for (Command command : commands) { Command c = execCommand(command); this.ces.addCommand(c); // calls method onProgressUpdate // which then call onCommandFinished publishProgress(c); } return this.ces; } catch (Exception e) { e.printStackTrace(); // exceptions in CommandExecutionSummary can be retrieved in GUI classes this.ces.addException(e); return this.ces; } finally { // make sure to close the connection this.jschSession.disconnect(); } }
Docker2go leverages a listener pattern as seen in the above example.
The listener class is a parameter of executeCommand()
which will then call the methods onCommandFinished()
or onAllCommandsFinished()
depending of the state of the AsyncTask.
The results of the Commands are part of the Command itself which is returned by the onCommandFinished()
method.
Command and Docker objects paradigm¶
Docker2go depends highly on the use of interfaces.
The Command
is one of those abstract interfaces.
The Command interface is implemented by DockerCommandBuilder
which lets you build your command
by utilizing a fluent API.
DockerCommandBuilder command = new DockerCommandBuilder() .apiEndpoint("/containers/json") .queryParam("all", "true") .requestMethod("GET"); parentActivity.activeConnection.executeCommand(this, command);
The this
in the code snippet above is the listener class described in seciton Multithreading.
Special in Docker2go's implementation is, that the GUI classes never talk directly to the AsyncTask
in the background. Every operation is cooridinated by the Connection class.
The results of the command are stored as string and can be accessed by calling the getResult()
method of the Command
.
The results are in JSON format which gets parsed by one of DockerObjParser
static methods.
DockerObjParser utilizes the Gson library to make the parsing process more convinient.
@Override public void onCommandFinished(Command command) { if (command.exitedAsExpected()) { DockerContainer[] dContainers = DockerObjParser.Containers(command.getResult()); containers.clear(); containers.addAll(Arrays.asList(dContainers)); containerArrayAdapter.notifyDataSetChanged(); } }
The example above shows how the list of containers is refreshed by overriding the onCommandFinished()
method.