Posted by Guillaume Laforge, Developer Advocate for Google Cloud
With the beta of the new Java 11 runtime for Google Cloud Functions, Java developers can now write their functions using the Java programming language (a language often used in enterprises) in addition to Node.js, Go, or Python. Cloud Functions allow you to run bits of code locally or in the cloud, without provisioning or managing servers: Deploy your code, and let the platform handle scaling up and down for you. Just focus on your code: handle incoming HTTP requests or respond to some cloud events, like messages coming from Cloud Pub/Sub or new files uploaded in Cloud Storage buckets.
In this article, let’s focus on what functions look like, how you can write portable functions, how to run and debug them locally or deploy them in the cloud or on-premises, thanks to the Functions Framework, an open source library that runs your functions. But you will also learn about third-party frameworks that you might be familiar with, that also let you create functions using common programming paradigms.
There are two types of functions: HTTP functions, and background functions. HTTP functions respond to incoming HTTP requests, whereas background functions react to cloud-related events.
The Java Functions Framework provides an API that you can use to author your functions, as well as an invoker which can be called to run your functions locally on your machine, or anywhere with a Java 11 environment.
To get started with this API, you will need to add a dependency in your build files. If you use Maven, add the following dependency tag in pom.xml:
pom.xml
<dependency> <groupId>com.google.cloud.functions</groupId> <artifactId>functions-framework-api</artifactId> <version>1.0.1</version> <scope>provided</scope> </dependency>
If you are using Gradle, add this dependency declaration in build.gradle:
build.gradle
compileOnly("com.google.cloud.functions:functions-framework-api")
A Java function that receives an incoming HTTP request implements the HttpFunction interface:
HttpFunction
import com.google.cloud.functions.*; import java.io.*; public class Example implements HttpFunction { @Override public void service(HttpRequest request, HttpResponse response) throws IOException { var writer = response.getWriter(); writer.write("Hello developers!"); } }
The service() method provides an HttpRequest and an HttpResponse object. From the request, you can get information about the HTTP headers, the payload body, or the request parameters. It’s also possible to handle multipart requests. With the response, you can set a status code or headers, define a body payload and a content-type.
service()
HttpRequest
HttpResponse
Background functions respond to events coming from the cloud, like new Pub/Sub messages, Cloud Storage file updates, or new or updated data in Cloud Firestore. There are actually two ways to implement such functions, either by dealing with the JSON payloads representing those events, or by taking advantage of object marshalling thanks to the Gson library, which takes care of the parsing transparently for the developer.
With a RawBackgroundFunction, the responsibility is on you to handle the incoming cloud event JSON-encoded payload. You receive a JSON string, so you are free to parse it however you like, with your JSON parser of your choice:
import com.google.cloud.functions.Context; import com.google.cloud.functions.RawBackgroundFunction; public class RawFunction implements RawBackgroundFunction { @Override public void accept(String json, Context context) { ... } }
But you also have the option to write a BackgroundFunction which uses Gson for unmarshalling a JSON representation into a Java class (a POJO, Plain-Old-Java-Object) representing that payload. To that end, you have to provide the POJO as a generic argument:
import com.google.cloud.functions.Context; import com.google.cloud.functions.BackgroundFunction; public class PubSubFunction implements BackgroundFunction<PubSubMsg> { @Override public void accept(PubSubMsg msg, Context context) { System.out.println("Received message ID: " + msg.messageId); } } public class PubSubMsg { String data; Map<String, String> attributes; String messageId; String publishTime; }
The Context parameter contains various metadata fields like timestamps, the type of events, and other attributes.
Which type of background function should you use? It depends on the control you need to have on the incoming payload, or if the Gson unmarshalling doesn’t fully fit your needs. But having the unmarshalling covered by the framework definitely streamlines the writing of your function.
Coding is always great, but seeing your code actually running is even more rewarding. The Functions Framework comes with the API we used above, but also with an invoker tool that you can use to run functions locally. For improving developer productivity, having a direct and local feedback loop on your own computer makes it much more comfortable than deploying in the cloud for each change you make to your code.
If you’re building your functions with Maven, you can install the Function Maven plugin in your pom.xml:
<plugin> <groupId>com.google.cloud.functions</groupId> <artifactId>function-maven-plugin</artifactId> <version>0.9.2</version> <configuration> <functionTarget>com.example.Example</functionTarget> </configuration> </plugin>
On the command-line, you can then run:
$ mvn function:run
You can pass extra parameters like --target to define a different function to run (in case your project contains several functions), --port to specify the port to listen to, or --classpath to explicitly set the classpath needed by the function to run. These are the parameters of the underlying Invoker class. However, to set these parameters via the Maven plugin, you’ll have to pass properties with -Drun.functionTarget=com.example.Example and -Drun.port.
--target
--port
--classpath
Invoker
-Drun.functionTarget=com.example.Example
-Drun.port
With Gradle, there is no dedicated plugin, but it’s easy to configure build.gradle to let you run functions.
First, define a dedicated configuration for the invoker:
configurations { invoker }
In the dependencies, add the Invoker library:
dependencies { invoker 'com.google.cloud.functions.invoker:java-function-invoker:1.0.0-beta1' }
And then, create a new task to run the Invoker:
tasks.register("runFunction", JavaExec) { main = 'com.google.cloud.functions.invoker.runner.Invoker' classpath(configurations.invoker) inputs.files(configurations.runtimeClasspath, sourceSets.main.output) args('--target', project.findProperty('runFunction.target') ?: 'com.example.Example', '--port', project.findProperty('runFunction.port') ?: 8080 ) doFirst { args('--classpath', files(configurations.runtimeClasspath, sourceSets.main.output).asPath) } }
By default, the above launches the function com.example.Example on port 8080, but you can override those on the command-line, when running gradle or the gradle wrapper:
com.example.Example
$ gradle runFunction -PrunFunction.target=com.example.HelloWorld \ -PrunFunction.port=8080
What’s interesting about the Functions Framework is that you are not tied to the Cloud Functions platform for deploying your functions. As long as, in your target environment, you can run your functions with the Invoker class, you can run your functions on Cloud Run, on Google Kubernetes Engine, on Knative environments, on other clouds when you can run Java, or more generally on any servers on-premises. It makes your functions highly portable between environments. But let’s have a closer look at deployment now.
You can deploy functions with the Maven plugin as well, with various parameters to tweak for defining regions, memory size, etc. But here, we’ll focus on using the cloud SDK, with its gcloud command-line, to deploy our functions.
gcloud
For example, to deploy an HTTP function, you would type:
$ gcloud functions deploy exampleFn \ --region europe-west1 \ --trigger-http \ --allow-unauthenticated \ --runtime java11 \ --entry-point com.example.Example \ --memory 512MB
For a background function that would be notified of new messages on a Pub/Sub topic, you would launch:
$ gcloud functions deploy exampleFn \ --region europe-west1 \ --trigger-topic msg-topic \ --runtime java11 \ --entry-point com.example.PubSubFunction \ --memory 512MB
Note that deployments come in two flavors as well, although the above commands are the same: functions are deployed from source with a pom.xml and built in Google Cloud, but when using a build tool other than Maven, you can also use the same command to deploy a pre-compiled JAR that contains your function implementation. Of course, you’ll have to create that JAR first.
So far, we looked at Java and the plain Functions Framework, but you can definitely use alternative JVM languages such as Apache Groovy, Kotlin, or Scala, and third-party frameworks that integrate with Cloud Functions like Micronaut and Spring Boot!
Without covering all those combinations, let’s have a look at two examples. What would an HTTP function look like in Groovy?
The first step will be to add Apache Groovy as a dependency in your pom.xml:
<dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>3.0.4</version> <type>pom</type> </dependency>
You will also need the GMaven compiler plugin to compile the Groovy code:
<plugin> <groupId>org.codehaus.gmavenplus</groupId> <artifactId>gmavenplus-plugin</artifactId> <version>1.9.0</version> <executions> <execution> <goals> <goal>addSources</goal> <goal>addTestSources</goal> <goal>compile</goal> <goal>compileTests</goal> </goals> </execution> </executions> </plugin>
When writing the function code, just use Groovy instead of Java:
import com.google.cloud.functions.* class HelloWorldFunction implements HttpFunction { void service(HttpRequest request, HttpResponse response) { response.writer.write "Hello Groovy World!" } }
The same explanations regarding running your function locally or deploying it still applies: the Java platform is pretty open to alternative languages too! And the Cloud Functions builder will happily build your Groovy code in the cloud, since Maven lets you compile this code thanks to the Groovy library.
Third-party frameworks also offer a dedicated Cloud Functions integration. Let’s have a look at Micronaut.
Micronaut is a “modern, JVM-based, full-stack framework for building modular, easily testable microservice and serverless applications”, as explained on its website. It supports the notion of serverless functions, web apps and microservices, and has a dedicated integration for Google Cloud Functions.
In addition to being a very efficient framework with super fast startup times (which is important, to avoid long cold starts on serverless services), what’s interesting about using Micronaut is that you can use Micronaut’s own programming model, including Dependency Injection, annotation-driven bean declaration, etc.
For HTTP functions, you can use the framework’s own @Controller / @Get annotations, instead of the Functions Framework’s own interfaces. So for example, a Micronaut HTTP function would look like:
@Controller
@Get
import io.micronaut.http.annotation.*; @Controller("/hello") public class HelloController { @Get(uri="/", produces="text/plain") public String index() { return "Example Response"; } }
This is the standard way in Micronaut to define a Web microservice, but it transparently builds upon the Functions Framework to run this service as a Cloud Function. Furthermore, this programming model offered by Micronaut is portable across other environments, since Micronaut runs in many different contexts.
Last but not least, if you are using the Micronaut Launch project (hosted on Cloud Run) which allows you to scaffold new projects easily (from the command-line or from a nice UI), you can opt for adding the google-cloud-function support module, and even choose your favorite language, build tool, or testing framework:
google-cloud-function
Be sure to check out the documentation for the Micronaut Cloud Functions support, and Spring Cloud Function support.
Now it’s your turn to try Cloud Functions for Java 11 today, with your favorite JVM language or third-party frameworks. Read the getting started guide, and try this for free with Google Cloud Platform free trial. Explore Cloud Functions’ features and use cases, take a look at the quickstarts, perhaps even contribute to the open source Functions Framework. And we’re looking forward to seeing what functions you’re going to build on this platform!
Sarah Clark, Program Manager, Google Developer Training
Front-end web developers face challenges when using common “asynchronous” requests. These requests, such as fetching a URL or reading a file, often lead to complicated code, especially when performing multiple actions in a row. How can we make this easier for developers?
Javascript Promises are a new tool that simplifies asynchronous code, converting a tangle of callbacks and event handlers into simple, straightforward code such as: fetch(url).then(decodeJSON).then(addToPage)...
fetch(url).then(decodeJSON).then(addToPage)...
Promises are used by many new web standards, including Service Worker, the Fetch API, Quota Management, Font Load Events,Web MIDI, and Streams.
We’ve just opened up a online course on Promises, built in collaboration with Udacity. This brief course, which you can finish in about a day, walks you through building an “Exoplanet Explorer” app that reads and displays live data using Promises. You’ll also learn to use the Fetch API and finally kiss XMLHttpRequest goodbye!
This short course is a prerequisite for most of the Senior Web Developer Nanodegree. Whether you are in the paid Nanodegree program or taking the course for free, won’t you come learn to make your code simpler and more reliable today?
Originally posted on Google Apps Developers blog
Posted by Matt Hessinger, Project Specialist, Google Apps Script
Welcome to our 100th blog post on Apps Script! It’s amazing how far we’ve come from our first post back in 2010. We started out highlighting some of the simple ways that you could develop with the Apps platform. Today, we’re sharing tips and best practices for developing more complex Apps Script solutions by pointing out some community contributions.
The Apps Script editor does not allow you to use your own source code management tool, making it a challenge to collaborate with other developers. Managing development, test, and production versions of a project becomes very tedious. What if you could have the best of both worlds — the powerful integration with Google’s platform that Apps Script offers, along with the development tooling and best practices that you use every day? Now, you can.
npm install -g node-google-apps-script
This project, “node-google-apps-script”, is a Node.js based command-line interface (CLI) that uses Google Drive API to update Apps Script project from the command line. You can view the node package on the NPM site, and also view the GitHub repo. Both links have usage instructions. This tool was created by Dan Thareja, with additional features added by Matt Condon.
Before using the tool, take a look at the Apps Script Importing and Exporting Projects page. There are a few things that you should be aware of as you plan out your development process. There are also a few best practices that you can employ to take full advantage of developing in this approach.
There is a sample project that demonstrates some of the practices described in this post: click here to view that code on GitHub. To get all of the Apps Script samples, including this import/export development example:
Your standalone Apps Script projects live in Google Drive. If you use a command-line interface (CLI) tool like the one linked above, you can work in your favorite editor, and commit and sync code to your chosen repository. You can add tasks in your task runner to push code up to one or more Apps Script projects, conditionally including or excluding code for different environments, checking coding style, linting, minifying, etc. You can more easily create and push UI-related files to a file host outside of Apps Script, which could be useful if those same files are used in other apps you are building.
In addition to the information on the Importing and Exporting Projects page, here are a few things to consider:
Over and above the editing experience, the biggest improvements you get by working outside the script editor is that you are no longer locked into working in just one Apps Script project. You can much more easily collaborate as a team, with individual developers having their own working Apps Script projects, while also having more controlled test, user acceptance and production versions, each with more process and security. Beyond just the consistency with other normal project practices, there are a few Apps Script specific ways you can leverage this multi-environment approach.
If you are going to use this approach, here are three best practices to consider:
The provided sample shows a simple example of how a base configuration class could allow a developer to inject their local values for their own debugging and testing. In this case, the developer also added the annotation @NotOnlyCurrentDoc, which tells Apps Script that they need the full scope for Drive API access. In this project, the “production” deployment has the annotation @OnlyCurrentDoc, which leads to the OAuth scope that is limited to the document associated with script running as Sheets, Docs, or Forms add-on. If you add a standard file pattern to the source project’s “ignore” file, these developer-specific files will never get into the actual codebase.
Benefits for your project — Production can have more limited OAuth scopes, while a developer can use broader access during development. Developers can also have their own personal configuration settings to support their individual development efforts.
While there is no current way to trigger tests in an automated way, you still may want to author unit tests that validate specific functions within your projects. You’ll also likely have specific configuration values for testing. Once again, none of these files should make it into a production deployment. You can even use the Apps Script Execution API to drive those tests from a test runner!
Benefits for your project — You can author test functions, and keep them separate from the production Apps Script file. This slims down your production Apps Script project, and keeps the correct OAuth scopes that are needed for production users.
If you are developing an add-on for Sheets or Docs, and you expect to have an “active” item on the SpreadsheetApp. However when you are developing or testing, you may be running your Apps Script without an “active” context. If you need to develop in this mode, you can wrap the call to get the current active item in a method that also can determine what mode you are running in. This would allow your development or test instance to inject the ID of an “active” document to use for testing, while delegating to the getActive* result when running in a real context.
Benefits for your project — You can integrate better unit testing methodologies into your projects, even if the end deployment state dependents on resources that aren’t typically available when debugging.
You now have the option to use your own development and source management tools. While you still do need to use the Apps Script editor in your application’s lifecycle — to publish as a web app or add-on, configure advanced services, etc. — taking this step will help you get the most out of the power of the Apps Script platform. Remember to check out Apps Script on the Google Developers site to get more information and samples for your Apps Script development.
If you happen to use python tools on the command line to facilitate your team’s build process, you can check out Joe Stump's python-gas-cli. You can view the package info here or the GitHub repo where you’ll also find usage instructions.
Here are some additional reference links related to this post:
Posted by Wouter van Oortmerssen, Fun Propulsion Labs at Google*
Originally posted to the Google Open Source blog
After months in development, the FlatBuffers 1.1 update is here. Originally released in June 2014, it’s a highly efficient open source cross-platform serialization library that allows you to read data without parsing/unpacking or allocating additional memory. It supports schema evolution (forwards/backwards compatibility) and optional JSON conversion. We primarily created it for games written in C++ where performance is critical, but it’s also useful more broadly. This update brings:
Download the latest release from our github page and join our discussion list for more details.
*Fun Propulsion Labs is a team within Google that's dedicated to advancing gaming on Android and other platforms.
public static void invokeExample() { String s; MethodType mt; MethodHandle mh; MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(String.class, char.class, char.class); MethodHandle mh = lookup.findVirtual(String.class, "replace", mt); s = (String) mh.invokeExact("App Engine Java 6 runtime",'6','7'); System.out.println(s); }
public static void viewTable(Connection con, String query) throws SQLException { try ( Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(query) ) { while (rs.next()) { // process results // } } catch (SQLException e) { // con resource is auto-closed, no need to do anything here! // } }
Map<String, List<String>> myMap = new HashMap<>();
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
public class SuperHeroes { public List listSuperHeroes() { List list = new ArrayList(); list.add(new SuperHero ("Champion of the Obvious", "Brad Abrams")); list.add(new SuperHero ("Mr. Justice", "Chris Ramsdale")); return list; } }
@Api(name = "superheroes", version = "v1") public class SuperHeroesV1 { ... }
$ curl http://localhost:8888/_ah/api/superheroes/v1/superheroes { "items": [ { "knownAs" : "Champion of the Obvious", "realName" : "Brad Abrams" }, { "knownAs" : "Mr. Justice", "realName" : "Chris Ramsdale" }
Real result = superheroes.list().execute();
GTLQuerySuperHeroesV1 *query = [GTLQuerySuperHeroesV1 queryForSuperHeroesList]; [service executeQuery:query completionHandler:^(GTLServiceTicket *ticket, GTLSuperHeroes *object, NSError *error) { NSArray *items = [object items]; }];
// ... var ROOT = 'https://' + window.location.host + '/_ah/api'; gapi.client.load('superheroes', 'v1', loadSuperheroesCallback, ROOT); // Get the list of superheroes gapi.client.superheroes.superheroes.list().execute(function(resp) { displaySuperheroesList(resp); });
// Set up the HTTP transport and JSON factoryHttpTransport httpTransport = new NetHttpTransport();JsonFactory jsonFactory = new JacksonFactory();// Set up OAuth 2.0 access of protected resources // using the refresh and access tokens, automatically // refreshing the access token when it expiresGoogleAccessProtectedResource requestInitializer = new GoogleAccessProtectedResource(accessToken, httpTransport, jsonFactory, clientId, clientSecret, refreshToken);// Set up the main Google+ classPlus plus = new Plus(httpTransport, requestInitializer, jsonFactory);// Make a request to access your profile and display it to consolePerson profile = plus.people().get("me").execute();System.out.println("ID: " + profile.getId());System.out.println("Name: " + profile.getDisplayName());System.out.println("Image URL: " + profile.getImage().getUrl());System.out.println("Profile URL: " + profile.getUrl());
When Google acquired Instantiations in August 2010, everyone knew about our Java Eclipse products. Shortly after we joined, we talked about how best to help developers now that we are part of Google. We have always wanted to get these tools in more developers’ hands. So, back in September we decided to give them away for free! The community response has been fantastic. With that done, we asked ourselves, how could we make a good thing even better? How about by open sourcing the code and creating two new Eclipse projects!
Today we are announcing Google’s donation of the source code and IP for two of these products to the open source community through the Eclipse Foundation. This donation includes WindowBuilder, the leading Eclipse Java GUI Designer, and CodePro Profiler, which identifies Java code performance issues. Specifically, the WindowBuilder Engine and designers for SWT and Swing. All in all, this is a value of more than $5 million dollars worth of code and IP.
The Eclipse Foundation’s Executive Director, Mike Milinkovich, states that, “this is clearly a significant new project announcement, and very good news for Java developers using Eclipse. It has been impressive to see the continued growth and popularity of WindowBuilder, as this product has always filled a much needed gap in the Eclipse offerings. We look forward to it appearing in an Eclipse release soon. We’re very pleased with Google’s generous support of Eclipse, and the Java developer community around the world.”
One of the exciting aspects of innovating in the open source arena is that customers benefit from a full community. We are very excited to see the diverse collection of companies and individuals that have already expressed an interest in contributing to these projects. Commercial level support is important to many customers. Genuitec, makers of MyEclipse, intends to offer commercial support for the various WindowBuilder based products including the SWT, Swing Designer and even the GWT Designer from Google. Please sign up on the Genuitec site for more information. Similarly, OnPositive intends to offer commercial support for CodePro Profiler, as well as lead as the committers on the Eclipse Community Project. Sign up on the OnPositive site for more information.
"Genuitec is pleased to offer commercial support for WindowBuilder-based products - Swing, SWT and GWT - in early 2011 for companies who wish to continue a paid support contract once their Google support expires. We've been involved with the Eclipse Foundation since the beginning, so we are very familiar with these products. Thus, providing commercial support for this product line is a natural fit for us," said Maher Masri, President of Genuitec.
“Over the years OnPositive has built up unique experience with the CodePro Profiler and we are excited to offer commercial support for it. Google’s donation ensures that Java developers can build faster applications,” said Pavel Petrochenko, President of OnPositive.
WindowBuilderWindowBuilder is regarded as the leading GUI builder in the Java community (winning the award for Best Commercial Eclipse Tool in 2009). It includes powerful functionality for creating user interfaces based on the popular Swing, SWT (Standard Widget Toolkit), GWT (Google Web Toolkit), RCP and XWT UI frameworks.
CodePro ProfilerCodePro Profiler is an Eclipse-based Java application profiling tool that helps developers identify performance issues early in the development cycle and find CPU and algorithmic bottlenecks, memory leaks, threading issues, and other concurrency-related problems that can slow down an application or cause it to hang.
Both WindowBuilder and CodePro Profiler will become Eclipse projects in the first half of 2011. Once each one is set up as a project and available for download from the Eclipse site, the products will be accessible to use as open source code under the the standard Eclipse license. I am looking forward to leading the WindowBuilder project.
If you have any questions, you can learn more at this FAQ or we look forward to hearing from you on the forums.
(Cross-posted from the Google Web Toolkit blog)
In early August, Google acquired Instantiations, a company known for its focus on Eclipse Java developer tools, including GWT Designer. We're happy to announce today that we're relaunching the following former Instantiations products under the Google name and making them available to all developers at no charge:
Now that these products are available again, we hope you’ll start using them within your GWT projects. Meanwhile, our next step is to more deeply unify them into the GWT family of tools by blending the fantastic Instantiations technology into the Google Plugin for Eclipse (GPE). So, there’s much more to come, including things we’re pretty sure you’ll like, such as UiBinder support in GWT Designer.
You can download any of the tools from the GWT download page. If you have questions or comments we’d love to hear from you. The best place to discuss the tools above is at http://forums.instantiations.com. As always, continue to discuss GWT and GPE at the main GWT Group.
We would love to stay in better touch with you as we have more news about how we are integrating the Instantiations products into the Google Web Toolkit suite. Sign up if you’d like to receive email updates on these products and other developer tools.
CreateAlbumTx createAlbum = albumsFactory.createAlbum( "College Days", "The best days of my life!");Album album = createAlbum.getAlbum();albumsFactory.shareAlbumWithFriends(album);byte[] photo = captureImage();photosFactory.uploadPhoto(album, photo, ImageType.JPG, "My Best Buddies");
GetScrapsTx scraps = scrapFactory.getSelfScraps();for(int i = 0; i < scraps.getScrapCount(); ++i) { ScrapEntry scrap = scraps.getScrap(i); if(myFriends.contains(scrap.getFromUserId())) { scrapFactory.replyToScrap(scrap, "hey, thanks for remembering me!"); } else { scrapFactory.replyToScrap(scrap, "wanna be friends? [:)]"); friendsFactory.sendFriendRequest(scrap.getFromUserId()); }}
package com.blogpost.client;import com.google.gwt.core.client.EntryPoint;import com.google.gwt.user.client.Window;import com.google.gwt.user.client.ui.RootPanel;import com.google.gwt.visualization.client.AjaxLoader;import com.google.gwt.visualization.client.DataTable;import com.google.gwt.visualization.client.Query;import com.google.gwt.visualization.client.QueryResponse;import com.google.gwt.visualization.client.Query.Callback;import com.google.gwt.visualization.client.visualizations.AnnotatedTimeLine;public class BlogPost implements EntryPoint { public void onModuleLoad() { AjaxLoader.loadVisualizationApi(new Runnable(){ public void run() { Query query = Query.create("http://spreadsheets.google.com/pub?key=pCQbetd-CptH5QNY89vLtAg"); query.send(new Callback(){ public void onResponse(QueryResponse response) { if (response.isError()) { Window.alert("An error occured: " + response.getDetailedMessage()); } DataTable data = response.getDataTable(); AnnotatedTimeLine.Options options = AnnotatedTimeLine.Options.create(); options.setDisplayAnnotations(true); RootPanel.get().add(new AnnotatedTimeLine(data, options, "800px", "400px")); } }); }}, AnnotatedTimeLine.PACKAGE); }}