GWT (Google Web Toolkit)
This is a introductory/resource page for developers working on the RStudio GWT code.
- GWT aka "Google Web Toolkit", pronounced "gwit"
- A toolkit for writing web applications
- A compiler to create optimized JavaScript from Java sources
- A "dev-mode" watcher for "refresh the browser" incremental recompilation
- Widget toolkit for creating desktop application-like user interfaces
- RPC, internationalization, UI templates, code-generation, obfuscation, minification, resource bundling, etc.
- Originally released by Google in 2006, open-sourced in 2013
- Docs, tutorials: http://www.gwtproject.org/
- Source code: https://github.com/gwtproject/gwt
In the early 2000s, building large-scale AJAX web applications in JavaScript was tough. The language was immature, and the tooling even more so. Google created GWT so developers could leverage existing Java tooling to build large scale applications.
- JDK 8, 11, or 14
-
javac -version
to see version; set JAVA_HOME env variable if necessary to target a particular JDK
-
- A Java editor such as Eclipse or VSCode
- ant (https://ant.apache.org/)
Build using ant
from the src/gwt folder. Ant is configured with build.xml
.
On Linux and Mac, recommend using the shell script src/gwt/ant
rather than invoking ant
directly. The
script will attempt to locate and use a suitable already-installed JDK.
That is, use ./ant
instead of just ant
. On Windows, continue using ant
directly.
-
./ant
(full optimized build, takes a long time) -
./ant draft
(full build without most optimizations, significantly faster, though still slow) -
./ant devmode
(RStudio Server dev mode <-- best way to work, only available on Mac and Linux) -
./ant desktop
(RStudio Desktop dev mode) ./ant unittest
If you encounter errors while building GWT, try doing ./ant clean
then running the build command again.
There are sometimes issues switching between different types of builds, or if running builds with different
versions of the JDK (for example, if you build with 8, then try to do an incremental build with 11, it will fail).
- A named set of instructions to the GWT compiler
- Defined in files with
.gwt.xml
extension - Definitions corresponding to the ant targets found at
rstudio/src/org/rstudio/studio
:RStudio.gwt.xml
RStudioDesktopSuperDevMode.gwt.xml
RStudioDraft.gwt.xml
RStudioSuperDevMove.gwt.xml
RStudioTests.gwt.xml
- RStudio loads
src/gwt/www/index.htm
- This simple bootstrap page loads the GWT Javascript, invoking the entrypoint
RStudio.onModuleLoad()
inRStudio.java
- From here it determines which type of window this is in
RStudio.onDelayLoadApplication()
- For the main window, this ends up creating an
Application
object and invokingApplication.get()
- Code makes extensive use of dependency injection via the GIN framework (a GWT-specific implementation of the Guice framework for Java)
- Relevant files:
RStudioGinModule.java
-
RStudioGinModuleOverlay.java
(Pro-only) RStudioGinjector.java
- Clues that dependency injection is involved:
- @Inject
- @Singleton
- RStudioGinjector.INSTANCE.getFoo()
- A circular dependency between injected objects will hang RStudio
- When encountered, defer actual creation of dependency until needed using the
Provider<>
helper - Provides a proxy which which you can later invoke
.get()
to create/fetch the actual dependency, breaking the loop
- RStudio Pro/Workbench-only GWT UI uses dependency injection to inject a stub for open-source, and an implementation for Pro. Files to watch out for are those ending in
*Overlay.java
and `*Pro.java - Compare
RStudioGinModuleOverlay.java
in the open-source and pro repos
- EventBus allows one part of code to fire strongly typed events, and other code to subscribe to receiving those events
- The sender and receiver are unaware of each other's actual identity; the EventBus acts as an intermediary
- The EventBus is a singleton available via dependency injector
- To define an event, create a class that extends
GwtEvent<>
; lots of examples in the code
- Given an event such as:
public class ShowErrorMessageEvent extends GwtEvent<ShowErrorMessageEvent.Handler>
eventBus_.dispatchEvent(new ShowErrorMessageEvent(errorMessage));
public class Foo implements ShowErrorMessageEvent.Handler {
@Inject public Foo(GlobalDisplay globalDisplay) {
this.disp_ = globalDisplay;
eventBus.addHandler(ShowErrorMessageEvent.TYPE, this);
}
public void onShowErrorMessage(ShowErrorMessageEvent event) {
ErrorMessage errorMessage = event.getErrorMessage();
disp_.showErrorMessage(errorMessage.getTitle(), errorMessage.getMessage());
}
private final GlobalDisplay disp_;
}
- Commands are events triggered by the user, typically by clicking on a button or menu item, or hitting a keyboard shortcut
- Defined in
Commands.cmd.xml
, for example:
<cmd id="showAboutDialog"
label="About RStudio..."
menuLabel="A_bout RStudio"
rebindable="false"
windowMode="main"/>
-
Must also create a corresponding entry in Commands.java:
public abstract AppCommand showAboutDialog();
-
Handling a command is simple, add "on" to the command name and use
@Handler
:
@Handler
void onShowAboutDialog() { … }
- Note, the class implementing the handler must contain:
public interface Binder extends CommandBinder<Commands, SomeClass> {}
- The webpage can send messages to the rsession process, and receive an async response
- Messages are defined in
RemoteServer.java
:
private static final String LIST_GET = "list_get";
@Override
public void listGet(String listName,
ServerRequestCallback<JsArrayString> requestCallback) {
sendRequest(RPC_SCOPE, LIST_GET, listName, requestCallback);
}
- Inspect these messages via the "Show Internal Request Log" command and/or the browser devtools network tab
- Search for the name (e.g. "list_get") in the rsession C++ code to find implementation on that side
- The rsession can send messages to the page; this is done via long-polling by the page via the
get_events
RPC call - The list of client events is in:
ClientEvent.java
- Handlers and dispatching happen in: `ClientEventDispatcher.java; typically by emitting an event via the EventBus.
- The web page and the desktop process (e.g. RStudio.exe) can communicate with each other
- Discussed here
- GWT UI can be constructed programmatically, by allocating and connecting various Widget subclasses
- Alternatively, the
.ui.xml
template format can be used to declaratively construct UI - For example, see
AskSecretDialog.ui.xml
andAskSecretDialog.java
- http://www.gwtproject.org/doc/latest/DevGuideUiBinder.html
- RStudio consumes several JavaScript libraries, including Ace (text editor) and xterm.js (terminal emulator)
- Integration with JavaScript either with JSNI (JavaScript Native Interface; the older technique) or JSInterop
- Most of RStudio uses JSNI
- JSNI: http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html
- JSInterop: http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJsInterop.html
- We maintain a fork of GWT 2.9, primarily containing accessibility fixes and a few other small tweaks
- See this README for details: https://github.com/rstudio/rstudio/blob/main/src/gwt/tools/build-gwt-README.md
Developing
- Beginners guide
- RStudio Development
- Git conventions
- Accessibility
- Development with Vagrant
- Electron desktop
- GWT
- Internationalization (i18n)
- Node Native Modules
Issues
Personal development environment
- Installing RStudio Dependencies
- M1 Mac Dev Machine Setup
- Visual Markdown Editing
- IDE Development Using Visual Studio Code
Building
Coding standards
Tests
Other topics