BaseSpace Java SDK
Create Your First Java BaseSpace Application
BaseSpace offers a Java SDK for developers. It is used for BaseSpace application development and script development for Illumina’s cloud-based solution for next-generation sequencing analysis.
This SDK has the ability to authenticate an app, gain access to use data, and upload analysis back to BaseSpace. The purpose of this SDK is to provide easy-to-use methods and a strongly typed data model representing BaseSpace concepts in Java without having to deal directly with the JSON over HTTP BaseSpace API directly.
The BaseSpace Java SDK is available on GitHub and can be cloned by issuing the following Git command:
git clone git@github.com:basespace/basespace-java-sdk.git
Or a developer can go to:
https://github.com/basespace/basespace-java-sdk/
and download the BaseSpace Java SDK. This SDK will include all of the classes in the SDK as well as a few dependency .jar files For more detailed information, please read over the BaseSpace Java SDK Documentation.
The following steps must be performed when configuring a development environment for the BaseSpace Java SDK. These instructions assume the Eclipse IDE, but the project/dependency configuration can be mapped to the Java IDE of your choice:
Setting Up The Development Environment | |
---|---|
Step | Instructions |
1. Install Eclipse | The most common IDE for Java is Eclipse. Eclipse can be downloaded here. This particular version of Eclipse has Maven integration, which will automatically include dependencies that the SDK will need to compile. |
2. Create a project | In Eclipse, go to the Workbench. Then we click on File -> New -> Java Project. This will take us to another screen where we can name our project and specify the JRE, we choose the default (or most up-to-date) values and name our project. |
3a. Include the Java SDK Package (Method 1) | In the first method, we navigate to the location of the basespace-java-sdk using our computer's file explorer. We then further navigate to src -> main -> java. Here we should have the "com" folder. We can simply drag and drop this folder to the "src" folder in our Eclipse Project. Eclipse will ask whether to copy these files or create links, this step is up to the developer. Now, under "src" we should have a package called com.illumina.basespace with all of the classes listed under it. |
3b. Include the Java SDK Package (Method 2) | Using the second method, we first right click on src in our Project. Select New -> Package. This will bring up a box where we specify where to put the package and what to name it. Name this package com.illumina.basespace. This package should appear in the src of the Project. Now, we use our computer's file explorer to navigate to basespace-java-sdk -> src -> main -> java -> com -> illumina -> basespace. In this folder we see all of the classes listed. Simply select all of these classes and drag and drop them into the com.illumina.basespace package. |
4. Add the jar files to the Project (Note: Maven will automatically include these for us) | In the Package Explorer on the left side of Eclipse, we right click on the project that was just created and select Build Path -> Configure Build Path. This will take us to the Java Build Path. Here we click on Add External JARs then navigate to and select the five .jar files that are included with the Java SDK. The dependencies for this SDK are the following .jar files:
|
We are now ready to begin development using the BaseSpace Java SDK!
The clientId and clientSecret are unique to your app. They are used as an identifier for the API to know which app is asking for access and to return access specific to them.
Getting the Client Id and Secret | |
---|---|
Step | Instructions |
1. Go to the App Dashboard | Go to the App Dashboard in the BaseSpace Developer Portal |
2. Create a New Application | Click on "Create a new Application". Enter the fields requested. The Home Page URL is the home page of our app which gives information about the app, this is not the redirect URL where we have BaseSpace redirect the user to access and launch our app. Finally, Click "Create Application" |
3. Retrieve the client Id and client secret | We are now taken to the Details page of our app. The client_id and client_secret are shown here.
|
Note: Normally these steps would give us the scope
that our app would need to request access to a user's data, but let us continue with the assumption that we have the scope
defined for us
Now that our development environment is set up and we have the clientId and clientSecret for our app, we can begin creating our Java application. This guide will show how to create a Sample App.
These are the general steps that would occur when triggering an app in BaseSpace:
1. The user launches the application from BaseSpace
2. The user selects which context to run the application in (Projects, AppResults, Samples, etc)
3. The user grants our application access to this data
4. The user is redirected to our redirect uri, and the redirect uri has a query parameter called appsessionuri
which contains information about the context from which the user
5. Our application makes a GET call to that appsession id (found in the appsession uri, which looks like v1pre3/appsessions/{appsession id})
6. The References response field in the appsession will contain information about the context
7. Pull the context information out, it should be a Project, Run, AppResult, or Sample Id
8. Request appropriate access to the resource that the user selected through the scope
parameter, an example being read project 1
9. In this case our scope would be read project 1
, scope is described in more detail in BaseSpace Permissions.
Notes Regarding Triggering
More Methods will be added to handle application triggering from BaseSpace, currently this SDK does not handle launching from a Project, Sample, AppResult, or Run from BaseSpace. Instead, an application using this SDK would need to request BROWSE GLOBAL
as its scope then navigate through user data and request higher access to download or upload data
ApiConfiguration is a Java interface provided by the SDK which defines the information that must be provided in order for the Java SDK to connect to, authenticate against, and exchange information with the BaseSpace API. Your application must provide an implementation of this interface. Please see the JavaDoc included with the BaseSpace Java SDK for further information regarding the methods of this interface
For the purpose of this sample application, let us assume that our scope from the previous step was browse global
.
Create the Implementation class for ApiConfiguration | |
---|---|
Step | Instructions |
1. Create a new Java package | Create a new package in Eclipse for our application by right-clicking on src in our Project -> New -> Package. The name should be of the form com.{identifier1}.{appname}, so for example com.johndoe.testapp. |
2. Create a new Java class that implements ApiConfiguration | Create a new Class within this Package, call it BaseSpaceTestConfiguration.java, the Class should look like this:
public class BaseSpaceTestConfiguration implements ApiConfiguration
|
3. Import ApiConfiguration into your class | Import ApiConfiguration.java from the SDK into BaseSpaceTestConfiguration.java by adding the following above the class name:
import com.illumina.basespace.ApiConfiguration;
Note: If you used the new class wizard in Eclipse this import would be created automatically
|
At this point you should have a new class that implements ApiConfiguration. Below is a table of the information you will need to provide for each interface method for this example application.
ApiConfiguration Information To Provide: | |
---|---|
Method | Value |
getApiRootUri() | https://api.basespace.illumina.com |
getAccessTokenUriFragment() | /oauthv2/token |
getAuthorizationUriFragment() | /oauthv2/deviceauthorization |
getVersion() | v1pre3 |
getAuthorizationScope() | browse global |
getClientId() | The client id obtained in the section “Getting Your Client Id and Client Secret” |
getClientSecret() | The client secret obtained in the section “Getting Your Client Id and Client Secret” |
An example of the full implementation class:
package com.test;
import com.illumina.basespace.ApiConfiguration;
public class BaseSpaceTestConfiguration implements ApiConfiguration
{
@Override
public String getVersion()
{
return "v1pre3";
}
@Override
public String getClientId()
{
return "my client id";//Use the real client id you obtained
}
@Override
public String getClientSecret()
{
return "my client secret";//Use the real client secret you obtained
}
@Override
public String getApiRootUri()
{
return "https://api.basespace.illumina.com";
}
@Override
public String getAccessTokenUriFragment()
{
return "/oauthv2/token";
}
@Override
public String getAuthorizationScope()
{
return "browse global";
}
@Override
public String getProxyHost()
{
return null;
}
@Override
public int getProxyPort()
{
return 0;
}
@Override
public String getAuthorizationUriFragment()
{
return "/oauthv2/deviceauthorization";
}
@Override
public String getAccessToken()
{
return null;
}
@Override
public int getReadTimeout()
{
return 0;
}
@Override
public int getConnectionTimeout()
{
return 0;
}
@Override
public String getStoreRootUri()
{
return null;
}
}
Create a new class for the test application which will be the entry point with a main() method in the same package. For this example we are naming this class BaseSpaceTestApp
. Within the main method create a new instance of the BaseSpaceTestConfiguration class you created in the previous step. Example of the class is provided below:
package com.test;
public class BaseSpaceTestApp
{
public static void main(String[] args)
{
BaseSpaceTestConfiguration config = new BaseSpaceTestConfiguration();
}
}
The ApiClientManager is a singleton class whose job it is to create new ApiClient objects for a given configuration. The complete code is provided below:
package com.test;
public class BaseSpaceTestApp
{
public static void main(String[] args)
{
BaseSpaceTestConfiguration config = new BaseSpaceTestConfiguration();
ApiClient client = ApiClientManager.instance().createClient(config);
}
}
If using Eclipse, create a new Run Configuration for your project and select your test application class BaseSpaceTestApp
as the startup class. If using a different IDE, please perform the equivalent necessary steps to select a startup class and run the project within that IDE. Now run the project.
The BaseSpace Java SDK follows the OAuth device authentication workflow. The details of the OAuth mechanism is beyond the scope of this document, so for further information about OAuth and how BaseSpace uses it please consult the BaseSpace documentation at: https://developer.basespace.illumina.com/docs/content/documentation/authentication/obtaining-access-tokens#ObtainingAccessTokens Because your ApiConfiguration implementation class provides a client id, client secret, and authorization scope, you must log into the MyIllumina.com website in order for the Java SDK to obtain an access token. The Java SDK will attempt to launch a web browser native to your platform so that you can enter your credentials. Once you have successfully logged in to MyIllumina, the SDK will obtain an access token which will then apply to all subsequent communication with the BaseSpace API through the SDK.
Alternatively, if your access token is known before hand, it can be provided to the getAccessToken() method of your ApiConfiguration implementation class, and the SDK will use that token for access instead of launching a browser and requiring login.
At this point you now have a reference to an instance of ApiClient, so you can now perform operations against the BaseSpace API via the SDK.
Once you have obtained an instance of ApiClient as outlined in the previous section, you can make calls against the BaseSpace API through the SDK ApiClient class.
Before getting further into some of the SDK functionality, it is recommended that SDK developers familiarize themselves with some of the following resources:
Noteworthy Resources | |
---|---|
Resource | Location |
The BaseSpace Data Model | https://developer.basespace.illumina.com/docs/content/documentation/rest-api/data-model-overview |
BaseSpace Java SDK Javadoc | Included in distribution |
Noteworthy Resources
BaseSpace Java SDK Javadoc Included in distribution
When the BaseSpace Java SDK makes a call against the BaseSpace API, it serializes its data into the JSON format and de-serializes it back from JSON to strongly typed objects when the response is received. A response can either return a single object or an array of objects.
Single Object Response
The singular version of a response object is named with the following convention:
GetXXXResponse
Where XXX is the entity type of the response. For example:
GetProjectResponse resp = client.getProject("1005");
This response object will contain a single entity which can be obtained with the get() method
Project project = resp.get();
Array of objects Response
The array of objects response is named with the following convention:
ListXXXResponse
Where XXX is the entity type of the response. For example:
ListAppResultsResponse appResultResp = client.getAppResults(project, null);
This response object will contain an array of entities which can be obtained with the items() method
AppResultCompact[]appResults = appResultsResp.items();
A convention used in the BaseSpace API which is represented in the Java SDK is the concept of a compact vs. full entity. For performance reasons, the compact version of an entity has a less information and thus a smaller payload in its response and is typically returned in an array of objects response. If your application requires more information about the entity, it should call the single object method of the SDK to obtain the full entity. In the data model of the SDK, every full version of an entity extends the compact version. The following snippet obtains an array of compact entities and iterates over them, obtaining the full version:
ListAppResultsResponse appResultsResp = client.getAppResults(project, null);
AppResultCompact[]appResults = appResultsResp.items();
for(AppResultCompact compact:appResults)
{
AppResult fullAppResult = client.getAppResult(compact.getId()).get();
}
Typically the ApiClient methods that return an array of objects response will take a QueryParams
object as one of its parameters. The QueryParams
object is useful for limiting the number and scope of the results. For example you can limit the number of results or specify a starting offset for the results, which is useful if you are implementing a paging table in your application’s UI etc. Passing null as a parameter to a method which expects QueryParams
will result in the BaseSpace API falling back to a default number of results, which may or may not be desired.
Below are some of the more common operations a developer may perform using the BaseSpace Java SDK with some code examples. All of the examples assume you have already obtained an ApiClient object using the steps outlined in the Create Your First BaseSpace Application section of this document.
The following example retrieves the current user associated with the access token granted to the ApiClient
User user = client.getCurrentUser().get();
System.out.println("User " + user.getName() + " at email " + user.getEmail());
ProjectCompact projects[] = client.getProjects(new QueryParams(10)).items();
for(ProjectCompact project:projects)
{
System.out.println("Project " + project.getName() + " owned by " + project.getUserOwnedBy().getName());
Project project = client.getProject(projectCompact.getId()).get();
System.out.println("App results for this project: " + project.getAppResultsHref().toString());
}
FileParams fileParams = new FileParams(new String[]{".bam",".vcf"},10);
AppResultCompact[]appResults = client.getAppResults(project, new QueryParams(10)).items();
for(AppResultCompact appResultCompact:appResults)
{
FileCompact[]files = client.getFiles(appResultCompact, fileParams).items();
for(FileCompact file:files)
{
System.out.println("Got the file " + file.getName() +" within appresult " + appResultCompact.getName());
}
}
AppResult fullAppResult = client.getAppResult("1093").get();
FileParams fileParams = new FileParams(new String[]{".vcf"},5);
FileCompact[]files = client.getFiles(fullAppResult, fileParams).items();
for(FileCompact fileCompact:files)
{
//For a download we need the full file entity
final File file = client.getFile(fileCompact.getId()).get();
//Local folder for a windows machine. File will be saved here with same name as
//BaseSpace file entity
final java.io.File localFolder = new java.io.File("C:\\BasespaceFiles");
client.download(file, localFolder, new DownloadListener()
{
@Override
public void progress(DownloadEvent evt)
{
System.out.println("We downloaded " + evt.getCurrentBytes() + " of " + evt.getTotalBytes());
}
@Override
public void complete(DownloadEvent evt)
{
System.out.println("Download is done");
assert(new java.io.File(localFolder,file.getName()).exists());
}
@Override
public void canceled(DownloadEvent evt)
{
}
});
}
To upload results back to BaseSpace, an app would perform the following steps:
The first step in your app is to choose an output Project for the user’s results to be upload into in BaseSpace. There are two options here, the app can either create a new Project each time or it can allow the user to specify an existing Project in BaseSpace for output that the user is the Owner of.
To check if a user is the owner of a Project, simply check the UserOwnedBy
field for that Project to ensure that it matches the currently logged in user’s Id.
In order to create a Project, the app’s access token must include the scope create projects
and in order to upload data to an existing Project, the app’s access token must include the scope write project {id}
.
Project item = client.createProject("TestProject created by Java SDK").get();
System.out.println("New ProjectId=" + item.getId());
AppResult newAppResult = client.createAppResult(project, "TestAppResult","Description",null, null).get();
System.out.println("Created appresult " + newAppResult.getId());
The example below shows how to perform a file upload to an appresult. For simplicity of example, we are uploading the entire file in a single part even though multi-part uploads are supported. Please consult BaseSpace documentation for details on the multipart upload functionality.
AppResult appresult = client.getAppResult(“12345”).get();
GetFileResponse resp = client.startMultipartUpload(appresult, "testfile.txt", "text/plain", null);
InputStream is = null;
try
{
java.io.File toUpload = new java.io.File("C:\\Files\\BasespaceUpload\\testfile.txt");
is = new FileInputStream(toUpload);
client.uploadFilePart(resp.get(), 1, null, is);
is.close();
is = null;
client.setMultipartUploadStatus(resp.get(), UploadStatus.Complete);
File theUploadedFile = client.getFile(resp.get().getId()).get();
assert(toUpload.length() == theUploadedFile.getSize().longValue());
}
catch(Throwable t)
{
throw new RuntimeException(t);
}
finally
{
//Just good practice
if (is != null) try
{
is.close();
}
catch (IOException e)
{
}
}
Some BaseSpace entities support properties. Please consult the BaseSpace documentation to learn which specific types support properties. The following example shows the addition of various properties to a project entity:
Create a new property list
List<Property<?>>properties = new ArrayList<Property<?>>();
Add a single string property
properties.add(new StringProperty("mytestapp.metrics.magicnumber","myDesc","42"));
Add an array of strings property
String[]stringArray = new String[]{"item1","item2","item3"};
properties.add(new StringProperty("mytestapp.metrics.magicnumbers","a Description",stringArray));
Add an appresult reference property
AppResult appresult = client.getAppResult(get("appresult.id")).get();
AppResultReference refProp = new AppResultReference("mytestapp.inputs.appresults","AppresultTest",appresult);
properties.add(refProp);
Project project = client.getProject(get("project.id")).get();
ListPropertiesResponse resp = client.putProperties(project, properties.toArray(new Property[properties.size()]));