Java API Client
Glean's Java API client provides enterprise-ready integration for Java applications, with support for Spring Boot and traditional enterprise patterns.
glean-api-client
Official Java client for Glean's Client API
Installation
- Maven
- Gradle
<dependency>
<groupId>com.glean.api-client</groupId>
<artifactId>glean-api-client</artifactId>
<version>0.x.x</version>
</dependency>
implementation 'com.glean.api-client:glean-api-client:0.x.x'
Quick Start
import com.glean.api_client.glean_api_client.Glean;
import com.glean.api_client.glean_api_client.models.components.*;
import java.util.List;
public class GleanExample {
public static void main(String[] args) {
Glean client = Glean.builder()
.apiToken(System.getenv("GLEAN_API_TOKEN"))
.serverURL(System.getenv("GLEAN_SERVER_URL"))
.build();
var response = client.client().chat().create()
.chatRequest(ChatRequest.builder()
.messages(List.of(
ChatMessage.builder()
.fragments(List.of(
ChatMessageFragment.builder()
.text("What are our company values?")
.build()))
.build()))
.build())
.call();
if (response.chatResponse().isPresent()) {
System.out.println(response.chatResponse().get().text());
}
}
}
Core Features
Chat API
public class ChatService {
private final Glean client;
public ChatService(String apiToken, String serverURL) {
this.client = Glean.builder()
.apiToken(apiToken)
.serverURL(serverURL)
.build();
}
public String sendMessage(String message) {
var response = client.client().chat().create()
.chatRequest(ChatRequest.builder()
.messages(List.of(
ChatMessage.builder()
.fragments(List.of(
ChatMessageFragment.builder()
.text(message)
.build()))
.build()))
.build())
.call();
return response.chatResponse()
.map(ChatResponse::text)
.orElse("No response");
}
}
Search API
public class SearchService {
private final Glean client;
public SearchService(String apiToken, String serverURL) {
this.client = Glean.builder()
.apiToken(apiToken)
.serverURL(serverURL)
.build();
}
public List<SearchResult> search(String query) {
var response = client.client().search().search()
.searchRequest(SearchRequest.builder()
.query(query)
.pageSize(10)
.build())
.call();
return response.searchResponse()
.map(SearchResponse::results)
.orElse(List.of());
}
}
Spring Boot Integration
Configuration
@Configuration
@ConfigurationProperties(prefix = "glean")
public class GleanConfig {
private String apiToken;
private String serverURL;
// Getters and setters
public String getApiToken() { return apiToken; }
public void setApiToken(String apiToken) { this.apiToken = apiToken; }
public String getServerURL() { return serverURL; }
public void setServerURL(String serverURL) { this.serverURL = serverURL; }
}
@Configuration
@EnableConfigurationProperties(GleanConfig.class)
public class GleanAutoConfiguration {
@Bean
public Glean gleanClient(GleanConfig config) {
return Glean.builder()
.apiToken(config.getApiToken())
.serverURL(config.getServerURL())
.build();
}
}
REST Controller
@RestController
@RequestMapping("/api")
public class GleanController {
private final Glean gleanClient;
public GleanController(Glean gleanClient) {
this.gleanClient = gleanClient;
}
@PostMapping("/chat")
public ResponseEntity<String> chat(@RequestBody Map<String, String> request) {
try {
String message = request.get("message");
var response = gleanClient.client().chat().create()
.chatRequest(ChatRequest.builder()
.messages(List.of(
ChatMessage.builder()
.fragments(List.of(
ChatMessageFragment.builder()
.text(message)
.build()))
.build()))
.build())
.call();
String responseText = response.chatResponse()
.map(ChatResponse::text)
.orElse("No response");
return ResponseEntity.ok(responseText);
} catch (Exception e) {
return ResponseEntity.status(500).body("Error: " + e.getMessage());
}
}
}
Authentication
User-Scoped Tokens
Glean client = Glean.builder()
.apiToken("your-user-token")
.serverURL("https://your-server-id-be.glean.com")
.build();
Configuration Properties
# application.yml
glean:
api-token: ${GLEAN_API_TOKEN}
server-url: ${GLEAN_SERVER_URL}
OAuth Access Tokens
An OAuth access token is a bearer credential, so it goes in the same apiToken field:
Glean client = Glean.builder()
.apiToken(oauthAccessToken)
.serverURL("https://your-server-id-be.glean.com")
.build();
Tokens issued by the Glean OAuth Authorization Server (including tokens obtained via Dynamic Client Registration) are detected automatically and need no extra header.
Tokens issued by an external identity provider (Google, Okta, Azure, etc.) additionally require the X-Glean-Auth-Type: OAUTH header on each request. The builder has no per-request header option, so supply a custom HTTP client that adds it:
import com.glean.api_client.glean_api_client.utils.HTTPClient;
import com.glean.api_client.glean_api_client.utils.SpeakeasyHTTPClient;
import java.io.InputStream;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HTTPClient oauthClient = new SpeakeasyHTTPClient() {
@Override
public HttpResponse<InputStream> send(HttpRequest request)
throws java.io.IOException, InterruptedException, java.net.URISyntaxException {
return super.send(HttpRequest.newBuilder(request, (name, value) -> true)
.header("X-Glean-Auth-Type", "OAUTH")
.build());
}
};
Glean client = Glean.builder()
.apiToken(oauthAccessToken)
.serverURL("https://your-server-id-be.glean.com")
.client(oauthClient)
.build();
See the OAuth authentication guide for identity-provider setup.
Complete Example: Authorization Code with PKCE
This example uses Spring Security's OAuth2 Client. Point OAUTH_ISSUER at the Glean OAuth Authorization Server or your IdP. For a confidential client, enable PKCE with an authorization-request customizer (it is on by default for public clients).
# application.yml
spring:
security:
oauth2:
client:
registration:
glean:
provider: glean
client-id: ${OAUTH_CLIENT_ID}
client-secret: ${OAUTH_CLIENT_SECRET}
authorization-grant-type: authorization_code
scope: openid,offline_access,SEARCH # SEARCH lets the token call /search; offline_access requests a refresh token
redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
provider:
glean:
issuer-uri: ${OAUTH_ISSUER}
glean:
server-url: ${GLEAN_SERVER_URL}
// SecurityConfig.java — enable PKCE for a confidential client
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestCustomizers;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http, ClientRegistrationRepository repo)
throws Exception {
var resolver = new DefaultOAuth2AuthorizationRequestResolver(repo, "/oauth2/authorization");
resolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce());
http.oauth2Login(login -> login
.authorizationEndpoint(endpoint -> endpoint.authorizationRequestResolver(resolver)));
return http.build();
}
}
// SearchController.java — exchange the authorized client's token for a Glean call
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.glean.api_client.glean_api_client.Glean;
import com.glean.api_client.glean_api_client.models.components.*;
import java.util.List;
@RestController
public class SearchController {
@Value("${glean.server-url}")
private String serverURL;
@GetMapping("/search")
public List<SearchResult> search(
@RegisteredOAuth2AuthorizedClient("glean") OAuth2AuthorizedClient authorizedClient) {
String accessToken = authorizedClient.getAccessToken().getTokenValue();
// For external-IdP tokens, add a custom HTTPClient that sets
// X-Glean-Auth-Type: OAUTH (see "OAuth Access Tokens" above). Glean
// Authorization Server tokens need no extra header.
Glean glean = Glean.builder()
.apiToken(accessToken)
.serverURL(serverURL)
.build();
return glean.client().search().query()
.request(SearchRequest.builder()
.query("quarterly reports")
.pageSize(10)
.build())
.call()
.searchResponse()
.flatMap(SearchResponse::results)
.orElse(List.of());
}
}
Error Handling
public String safeChat(Glean client, String message) {
try {
var response = client.client().chat().create()
.chatRequest(ChatRequest.builder()
.messages(List.of(
ChatMessage.builder()
.fragments(List.of(
ChatMessageFragment.builder()
.text(message)
.build()))
.build()))
.build())
.call();
return response.chatResponse()
.map(ChatResponse::text)
.orElse("No response");
} catch (Exception e) {
System.err.println("API error: " + e.getMessage());
return "Sorry, I couldn't process your request.";
}
}
Testing
JUnit 5 with Mockito
@ExtendWith(MockitoExtension.class)
class ChatServiceTest {
@Mock
private Glean gleanClient;
@InjectMocks
private ChatService chatService;
@Test
void shouldSendMessageSuccessfully() {
String message = "Test message";
String expectedResponse = "Test response";
// Mock setup and test implementation
assertNotNull(expectedResponse);
}
}