# SkillPatch skill: azure-messaging-webpubsub-java

This skill enables agents to build real-time web applications using the Azure Web PubSub SDK for Java. It covers client creation with various authentication methods (connection string, access key, DefaultAzureCredential), as well as core messaging patterns like sending messages to all connections, filtered connections, and groups. It is well-suited for implementing WebSocket-based messaging, live updates, chat applications, and server-to-client push notifications.

You (the agent) were given this URL and asked to use this skill. This is a **public** skill — no SkillPatch account, API key, or prior setup is required. Two ways to use it:

**1. Use it right now** — the skill's complete file tree (SKILL.md + all reference files) is inlined below; read `SKILL.md` first, then follow it, consulting the other files as it directs.

**2. Install the exact package onto disk** (recommended if you can run a shell — this reproduces the skill byte-for-byte, including any binary assets that can't be inlined):

```bash
mkdir -p .claude/skills/azure-messaging-webpubsub-java
curl -sSL https://skillpatch.dev/install_skill/azure-messaging-webpubsub-java | tar -xz -C .claude/skills/
```

(`.claude/skills/` is Claude Code's convention; use whatever directory your agent loads skills from.)


---

## Skill files (2)

- `SKILL.md`
- `references/examples.md`


### `SKILL.md`

````markdown
---
name: azure-messaging-webpubsub-java
description: Build real-time web applications with Azure Web PubSub SDK for Java. Use when implementing WebSocket-based messaging, live updates, chat applications, or server-to-client push notifications.
license: MIT
metadata:
  author: Microsoft
  version: "1.0.0"
  package: com.azure:azure-messaging-webpubsub
---

# Azure Web PubSub SDK for Java

Build real-time web applications using the Azure Web PubSub SDK for Java.

## Installation

```xml
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-messaging-webpubsub</artifactId>
    <version>1.5.0</version>
</dependency>
```

## Client Creation

### With Connection String

```java
import com.azure.messaging.webpubsub.WebPubSubServiceClient;
import com.azure.messaging.webpubsub.WebPubSubServiceClientBuilder;

WebPubSubServiceClient client = new WebPubSubServiceClientBuilder()
    .connectionString("<connection-string>")
    .hub("chat")
    .buildClient();
```

### With Access Key

```java
import com.azure.core.credential.AzureKeyCredential;

WebPubSubServiceClient client = new WebPubSubServiceClientBuilder()
    .credential(new AzureKeyCredential("<access-key>"))
    .endpoint("<endpoint>")
    .hub("chat")
    .buildClient();
```

### With DefaultAzureCredential

```java
import com.azure.core.credential.TokenCredential;
import com.azure.identity.AzureIdentityEnvVars;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.identity.ManagedIdentityCredentialBuilder;

// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
TokenCredential credential = new DefaultAzureCredentialBuilder()
    .requireEnvVars(AzureIdentityEnvVars.AZURE_TOKEN_CREDENTIALS)
    .build();
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/java/api/overview/azure/identity-readme?view=azure-java-stable#credential-classes
// TokenCredential credential = new ManagedIdentityCredentialBuilder().build();

WebPubSubServiceClient client = new WebPubSubServiceClientBuilder()
    .credential(credential)
    .endpoint("<endpoint>")
    .hub("chat")
    .buildClient();
```

### Async Client

```java
import com.azure.messaging.webpubsub.WebPubSubServiceAsyncClient;

WebPubSubServiceAsyncClient asyncClient = new WebPubSubServiceClientBuilder()
    .connectionString("<connection-string>")
    .hub("chat")
    .buildAsyncClient();
```

## Key Concepts

- **Hub**: Logical isolation unit for connections
- **Group**: Subset of connections within a hub
- **Connection**: Individual WebSocket client connection
- **User**: Entity that can have multiple connections

## Core Patterns

### Send to All Connections

```java
import com.azure.messaging.webpubsub.models.WebPubSubContentType;

// Send text message
client.sendToAll("Hello everyone!", WebPubSubContentType.TEXT_PLAIN);

// Send JSON
String jsonMessage = "{\"type\": \"notification\", \"message\": \"New update!\"}";
client.sendToAll(jsonMessage, WebPubSubContentType.APPLICATION_JSON);
```

### Send to All with Filter

```java
import com.azure.core.http.rest.RequestOptions;
import com.azure.core.util.BinaryData;

BinaryData message = BinaryData.fromString("Hello filtered users!");

// Filter by userId
client.sendToAllWithResponse(
    message,
    WebPubSubContentType.TEXT_PLAIN,
    message.getLength(),
    new RequestOptions().addQueryParam("filter", "userId ne 'user1'"));

// Filter by groups
client.sendToAllWithResponse(
    message,
    WebPubSubContentType.TEXT_PLAIN,
    message.getLength(),
    new RequestOptions().addQueryParam("filter", "'GroupA' in groups and not('GroupB' in groups)"));
```

### Send to Group

```java
// Send to all connections in a group
client.sendToGroup("java-developers", "Hello Java devs!", WebPubSubContentType.TEXT_PLAIN);

// Send JSON to group
String json = "{\"event\": \"update\", \"data\": {\"version\": \"2.0\"}}";
client.sendToGroup("subscribers", json, WebPubSubContentType.APPLICATION_JSON);
```

### Send to Specific Connection

```java
// Send to a specific connection by ID
client.sendToConnection("connectionId123", "Private message", WebPubSubContentType.TEXT_PLAIN);
```

### Send to User

```java
// Send to all connections for a specific user
client.sendToUser("andy", "Hello Andy!", WebPubSubContentType.TEXT_PLAIN);
```

### Manage Groups

```java
// Add connection to group
client.addConnectionToGroup("premium-users", "connectionId123");

// Remove connection from group
client.removeConnectionFromGroup("premium-users", "connectionId123");

// Add user to group (all their connections)
client.addUserToGroup("admin-group", "userId456");

// Remove user from group
client.removeUserFromGroup("admin-group", "userId456");

// Check if user is in group
boolean exists = client.userExistsInGroup("admin-group", "userId456");
```

### Manage Connections

```java
// Check if connection exists
boolean connected = client.connectionExists("connectionId123");

// Close a connection
client.closeConnection("connectionId123");

// Close with reason
client.closeConnection("connectionId123", "Session expired");

// Check if user exists (has any connections)
boolean userOnline = client.userExists("userId456");

// Close all connections for a user
client.closeUserConnections("userId456");

// Close all connections in a group
client.closeGroupConnections("inactive-group");
```

### Generate Client Access Token

```java
import com.azure.messaging.webpubsub.models.GetClientAccessTokenOptions;
import com.azure.messaging.webpubsub.models.WebPubSubClientAccessToken;

// Basic token
WebPubSubClientAccessToken token = client.getClientAccessToken(
    new GetClientAccessTokenOptions());
System.out.println("URL: " + token.getUrl());

// With user ID
WebPubSubClientAccessToken userToken = client.getClientAccessToken(
    new GetClientAccessTokenOptions().setUserId("user123"));

// With roles (permissions)
WebPubSubClientAccessToken roleToken = client.getClientAccessToken(
    new GetClientAccessTokenOptions()
        .setUserId("user123")
        .addRole("webpubsub.joinLeaveGroup")
        .addRole("webpubsub.sendToGroup"));

// With groups to join on connect
WebPubSubClientAccessToken groupToken = client.getClientAccessToken(
    new GetClientAccessTokenOptions()
        .setUserId("user123")
        .addGroup("announcements")
        .addGroup("updates"));

// With custom expiration
WebPubSubClientAccessToken expToken = client.getClientAccessToken(
    new GetClientAccessTokenOptions()
        .setUserId("user123")
        .setExpiresAfter(Duration.ofHours(2)));
```

### Grant/Revoke Permissions

```java
import com.azure.messaging.webpubsub.models.WebPubSubPermission;

// Grant permission to send to a group
client.grantPermission(
    WebPubSubPermission.SEND_TO_GROUP,
    "connectionId123",
    new RequestOptions().addQueryParam("targetName", "chat-room"));

// Revoke permission
client.revokePermission(
    WebPubSubPermission.SEND_TO_GROUP,
    "connectionId123",
    new RequestOptions().addQueryParam("targetName", "chat-room"));

// Check permission
boolean hasPermission = client.checkPermission(
    WebPubSubPermission.SEND_TO_GROUP,
    "connectionId123",
    new RequestOptions().addQueryParam("targetName", "chat-room"));
```

### Async Operations

```java
asyncClient.sendToAll("Async message!", WebPubSubContentType.TEXT_PLAIN)
    .subscribe(
        unused -> System.out.println("Message sent"),
        error -> System.err.println("Error: " + error.getMessage())
    );

asyncClient.sendToGroup("developers", "Group message", WebPubSubContentType.TEXT_PLAIN)
    .doOnSuccess(v -> System.out.println("Sent to group"))
    .doOnError(e -> System.err.println("Failed: " + e))
    .subscribe();
```

## Error Handling

```java
import com.azure.core.exception.HttpResponseException;

try {
    client.sendToConnection("invalid-id", "test", WebPubSubContentType.TEXT_PLAIN);
} catch (HttpResponseException e) {
    System.out.println("Status: " + e.getResponse().getStatusCode());
    System.out.println("Error: " + e.getMessage());
}
```

## Environment Variables

```bash
WEB_PUBSUB_CONNECTION_STRING=Endpoint=https://<resource>.webpubsub.azure.com;AccessKey=...  # Alternative to Entra ID auth
WEB_PUBSUB_ENDPOINT=https://<resource>.webpubsub.azure.com  # Required for AzureKeyCredential or TokenCredential auth
WEB_PUBSUB_ACCESS_KEY=<your-access-key>  # Only required for AzureKeyCredential auth
AZURE_TOKEN_CREDENTIALS=prod  # Required only if DefaultAzureCredential is used in production
```

## Client Roles

| Role | Permission |
|------|------------|
| `webpubsub.joinLeaveGroup` | Join/leave any group |
| `webpubsub.sendToGroup` | Send to any group |
| `webpubsub.joinLeaveGroup.<group>` | Join/leave specific group |
| `webpubsub.sendToGroup.<group>` | Send to specific group |

## Best Practices

1. **Use Groups**: Organize connections into groups for targeted messaging
2. **User IDs**: Associate connections with user IDs for user-level messaging
3. **Token Expiration**: Set appropriate token expiration for security
4. **Roles**: Grant minimal required permissions via roles
5. **Hub Isolation**: Use separate hubs for different application features
6. **Connection Management**: Clean up inactive connections

## Trigger Phrases

- "Web PubSub Java"
- "WebSocket messaging Azure"
- "real-time push notifications"
- "server-sent events"
- "chat application backend"
- "live updates broadcasting"

````


### `references/examples.md`

````markdown
# Azure Web PubSub Java SDK - Examples

Comprehensive code examples for the Azure Web PubSub SDK for Java.

## Table of Contents
- [Maven Dependency](#maven-dependency)
- [Client Creation](#client-creation)
- [Send Messages](#send-messages)
- [Group Management](#group-management)
- [Connection Management](#connection-management)
- [Client Access Tokens](#client-access-tokens)
- [Permissions](#permissions)
- [Async Operations](#async-operations)
- [Complete Application Example](#complete-application-example)

## Maven Dependency

```xml
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-messaging-webpubsub</artifactId>
    <version>1.5.0</version>
</dependency>
```

## Client Creation

### With Connection String

```java
import com.azure.messaging.webpubsub.WebPubSubServiceClient;
import com.azure.messaging.webpubsub.WebPubSubServiceClientBuilder;

WebPubSubServiceClient client = new WebPubSubServiceClientBuilder()
    .connectionString(System.getenv("WEB_PUBSUB_CONNECTION_STRING"))
    .hub("chat")
    .buildClient();
```

### With DefaultAzureCredential

```java
import com.azure.identity.DefaultAzureCredentialBuilder;

WebPubSubServiceClient client = new WebPubSubServiceClientBuilder()
    .credential(new DefaultAzureCredentialBuilder().build())
    .endpoint(System.getenv("WEB_PUBSUB_ENDPOINT"))
    .hub("chat")
    .buildClient();
```

### With Access Key

```java
import com.azure.core.credential.AzureKeyCredential;

WebPubSubServiceClient client = new WebPubSubServiceClientBuilder()
    .credential(new AzureKeyCredential("<access-key>"))
    .endpoint("<endpoint>")
    .hub("chat")
    .buildClient();
```

### Async Client

```java
import com.azure.messaging.webpubsub.WebPubSubServiceAsyncClient;

WebPubSubServiceAsyncClient asyncClient = new WebPubSubServiceClientBuilder()
    .connectionString(connectionString)
    .hub("chat")
    .buildAsyncClient();
```

## Send Messages

### Send to All Connections

```java
import com.azure.messaging.webpubsub.models.WebPubSubContentType;

// Send text message
client.sendToAll("Hello everyone!", WebPubSubContentType.TEXT_PLAIN);

// Send JSON
String jsonMessage = "{\"type\": \"notification\", \"message\": \"New update!\"}";
client.sendToAll(jsonMessage, WebPubSubContentType.APPLICATION_JSON);
```

### Send to All with Filter

```java
import com.azure.core.http.rest.RequestOptions;
import com.azure.core.util.BinaryData;

BinaryData message = BinaryData.fromString("Hello filtered users!");

// Filter by userId
client.sendToAllWithResponse(
    message,
    WebPubSubContentType.TEXT_PLAIN,
    message.getLength(),
    new RequestOptions().addQueryParam("filter", "userId ne 'user1'"));

// Filter by groups
client.sendToAllWithResponse(
    message,
    WebPubSubContentType.TEXT_PLAIN,
    message.getLength(),
    new RequestOptions().addQueryParam("filter", "'GroupA' in groups and not('GroupB' in groups)"));
```

### Send to Group

```java
client.sendToGroup("developers", "Hello developers!", WebPubSubContentType.TEXT_PLAIN);

String json = "{\"event\": \"update\", \"version\": \"2.0\"}";
client.sendToGroup("subscribers", json, WebPubSubContentType.APPLICATION_JSON);
```

### Send to User

```java
client.sendToUser("user123", "Personal message", WebPubSubContentType.TEXT_PLAIN);
```

### Send to Connection

```java
client.sendToConnection("connectionId123", "Direct message", WebPubSubContentType.TEXT_PLAIN);
```

## Group Management

### Add/Remove Connections

```java
// Add connection to group
client.addConnectionToGroup("premium-users", "connectionId123");

// Remove connection from group
client.removeConnectionFromGroup("premium-users", "connectionId123");
```

### Add/Remove Users

```java
// Add user to group (all their connections)
client.addUserToGroup("admin-group", "userId456");

// Remove user from group
client.removeUserFromGroup("admin-group", "userId456");

// Check if user is in group
boolean exists = client.userExistsInGroup("admin-group", "userId456");
```

## Connection Management

### Check Connection/User Status

```java
// Check if connection exists
boolean connected = client.connectionExists("connectionId123");

// Check if user exists (has any connections)
boolean userOnline = client.userExists("userId456");
```

### Close Connections

```java
// Close a connection
client.closeConnection("connectionId123");

// Close with reason
client.closeConnection("connectionId123", "Session expired");

// Close all connections for a user
client.closeUserConnections("userId456");

// Close all connections in a group
client.closeGroupConnections("inactive-group");
```

## Client Access Tokens

### Generate Basic Token

```java
import com.azure.messaging.webpubsub.models.GetClientAccessTokenOptions;
import com.azure.messaging.webpubsub.models.WebPubSubClientAccessToken;

WebPubSubClientAccessToken token = client.getClientAccessToken(
    new GetClientAccessTokenOptions());
System.out.println("WebSocket URL: " + token.getUrl());
```

### Token with User ID

```java
WebPubSubClientAccessToken token = client.getClientAccessToken(
    new GetClientAccessTokenOptions().setUserId("user123"));
```

### Token with Roles

```java
WebPubSubClientAccessToken token = client.getClientAccessToken(
    new GetClientAccessTokenOptions()
        .setUserId("user123")
        .addRole("webpubsub.joinLeaveGroup")
        .addRole("webpubsub.sendToGroup"));
```

### Token with Initial Groups

```java
WebPubSubClientAccessToken token = client.getClientAccessToken(
    new GetClientAccessTokenOptions()
        .setUserId("user123")
        .addGroup("announcements")
        .addGroup("updates"));
```

### Token with Custom Expiration

```java
import java.time.Duration;

WebPubSubClientAccessToken token = client.getClientAccessToken(
    new GetClientAccessTokenOptions()
        .setUserId("user123")
        .setExpiresAfter(Duration.ofHours(2)));
```

## Permissions

### Grant/Revoke Permissions

```java
import com.azure.messaging.webpubsub.models.WebPubSubPermission;

// Grant permission
client.grantPermission(
    WebPubSubPermission.SEND_TO_GROUP,
    "connectionId123",
    new RequestOptions().addQueryParam("targetName", "chat-room"));

// Revoke permission
client.revokePermission(
    WebPubSubPermission.SEND_TO_GROUP,
    "connectionId123",
    new RequestOptions().addQueryParam("targetName", "chat-room"));

// Check permission
boolean hasPermission = client.checkPermission(
    WebPubSubPermission.SEND_TO_GROUP,
    "connectionId123",
    new RequestOptions().addQueryParam("targetName", "chat-room"));
```

## Async Operations

```java
WebPubSubServiceAsyncClient asyncClient = new WebPubSubServiceClientBuilder()
    .connectionString(connectionString)
    .hub("chat")
    .buildAsyncClient();

asyncClient.sendToAll("Async message!", WebPubSubContentType.TEXT_PLAIN)
    .subscribe(
        unused -> System.out.println("Message sent"),
        error -> System.err.println("Error: " + error.getMessage())
    );

asyncClient.sendToGroup("developers", "Group message", WebPubSubContentType.TEXT_PLAIN)
    .doOnSuccess(v -> System.out.println("Sent to group"))
    .doOnError(e -> System.err.println("Failed: " + e))
    .subscribe();
```

## Complete Application Example

### Real-Time Chat Service

```java
import com.azure.messaging.webpubsub.*;
import com.azure.messaging.webpubsub.models.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ChatService {
    private final WebPubSubServiceClient client;
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final Map<String, Set<String>> userRooms = new ConcurrentHashMap<>();
    
    public ChatService(String connectionString) {
        this.client = new WebPubSubServiceClientBuilder()
            .connectionString(connectionString)
            .hub("chat")
            .buildClient();
    }
    
    public WebPubSubClientAccessToken connectUser(String userId, List<String> initialRooms) {
        GetClientAccessTokenOptions options = new GetClientAccessTokenOptions()
            .setUserId(userId)
            .addRole("webpubsub.joinLeaveGroup")
            .addRole("webpubsub.sendToGroup")
            .setExpiresAfter(Duration.ofHours(24));
        
        // Add initial rooms
        for (String room : initialRooms) {
            options.addGroup(room);
        }
        
        userRooms.put(userId, new HashSet<>(initialRooms));
        
        return client.getClientAccessToken(options);
    }
    
    public void joinRoom(String userId, String roomId) {
        client.addUserToGroup(roomId, userId);
        userRooms.computeIfAbsent(userId, k -> new HashSet<>()).add(roomId);
        
        broadcastToRoom(roomId, new ChatEvent("user_joined", userId, roomId, null));
    }
    
    public void leaveRoom(String userId, String roomId) {
        client.removeUserFromGroup(roomId, userId);
        
        Set<String> rooms = userRooms.get(userId);
        if (rooms != null) {
            rooms.remove(roomId);
        }
        
        broadcastToRoom(roomId, new ChatEvent("user_left", userId, roomId, null));
    }
    
    public void sendMessage(String userId, String roomId, String message) {
        ChatEvent event = new ChatEvent("message", userId, roomId, message);
        broadcastToRoom(roomId, event);
    }
    
    public void sendDirectMessage(String fromUserId, String toUserId, String message) {
        ChatEvent event = new ChatEvent("direct_message", fromUserId, null, message);
        try {
            String json = objectMapper.writeValueAsString(event);
            client.sendToUser(toUserId, json, WebPubSubContentType.APPLICATION_JSON);
        } catch (Exception e) {
            throw new RuntimeException("Failed to send direct message", e);
        }
    }
    
    public void broadcastSystemMessage(String message) {
        ChatEvent event = new ChatEvent("system", "system", null, message);
        try {
            String json = objectMapper.writeValueAsString(event);
            client.sendToAll(json, WebPubSubContentType.APPLICATION_JSON);
        } catch (Exception e) {
            throw new RuntimeException("Failed to broadcast", e);
        }
    }
    
    public boolean isUserOnline(String userId) {
        return client.userExists(userId);
    }
    
    public void disconnectUser(String userId) {
        client.closeUserConnections(userId);
        userRooms.remove(userId);
    }
    
    private void broadcastToRoom(String roomId, ChatEvent event) {
        try {
            String json = objectMapper.writeValueAsString(event);
            client.sendToGroup(roomId, json, WebPubSubContentType.APPLICATION_JSON);
        } catch (Exception e) {
            throw new RuntimeException("Failed to broadcast to room", e);
        }
    }
    
    static class ChatEvent {
        public String type;
        public String userId;
        public String roomId;
        public String message;
        public long timestamp;
        
        ChatEvent(String type, String userId, String roomId, String message) {
            this.type = type;
            this.userId = userId;
            this.roomId = roomId;
            this.message = message;
            this.timestamp = System.currentTimeMillis();
        }
    }
}
```

### Usage

```java
public class Main {
    public static void main(String[] args) {
        String connectionString = System.getenv("WEB_PUBSUB_CONNECTION_STRING");
        ChatService chatService = new ChatService(connectionString);
        
        // Connect a user
        WebPubSubClientAccessToken token = chatService.connectUser(
            "user1", 
            Arrays.asList("general", "support")
        );
        System.out.println("WebSocket URL: " + token.getUrl());
        
        // Send a message
        chatService.sendMessage("user1", "general", "Hello everyone!");
        
        // Send direct message
        chatService.sendDirectMessage("user1", "user2", "Hey, private message!");
        
        // Broadcast system message
        chatService.broadcastSystemMessage("Server maintenance in 10 minutes");
    }
}
```

## Environment Variables

```bash
WEB_PUBSUB_CONNECTION_STRING=Endpoint=https://<resource>.webpubsub.azure.com;AccessKey=...
WEB_PUBSUB_ENDPOINT=https://<resource>.webpubsub.azure.com
WEB_PUBSUB_ACCESS_KEY=<your-access-key>
```

## Client Roles Reference

| Role | Permission |
|------|------------|
| `webpubsub.joinLeaveGroup` | Join/leave any group |
| `webpubsub.sendToGroup` | Send to any group |
| `webpubsub.joinLeaveGroup.<group>` | Join/leave specific group |
| `webpubsub.sendToGroup.<group>` | Send to specific group |

## Best Practices

1. **Use Groups** - Organize connections into groups for targeted messaging
2. **User IDs** - Associate connections with user IDs for user-level messaging
3. **Token Expiration** - Set appropriate token expiration for security
4. **Roles** - Grant minimal required permissions via roles
5. **Hub Isolation** - Use separate hubs for different application features
6. **Connection Management** - Clean up inactive connections
7. **Error Handling** - Handle connection failures gracefully

````
