Skip to content

Commit f283e45

Browse files
authored
Add bolt-jakarta-servlet and bolt-jakarta-jetty modules for Jakarta EE supports (#920)
1 parent 71ca605 commit f283e45

File tree

67 files changed

+5823
-37
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+5823
-37
lines changed

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,18 @@ All released versions are available on the Maven Central repositories. The lates
8383

8484
#### Bolt & Built-in Extensions
8585

86-
|groupId:artifactId|Description|
87-
|---|---|
88-
|[**com.slack.api:bolt**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt)|Bolt is a framework that offers an abstraction layer to build Slack apps safely and quickly. The most commonly used Servlet environment is supported out-of-the-box.|
89-
|[**com.slack.api:bolt-socket-mode**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-socket-mode)|This module offers a handy way to run Bolt apps through [Socket Mode](https://api.slack.com/) connections.|
90-
|[**com.slack.api:bolt-jetty**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-jetty)|This module offers a handy way to run Bolt apps on the [Jetty HTTP server](https://www.eclipse.org/jetty/).|
91-
|[**com.slack.api:bolt-aws-lambda**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-aws-lambda)|This module offers a handy way to run Bolt apps on AWS [API Gateway](https://aws.amazon.com/api-gateway/) + [Lambda](https://aws.amazon.com/lambda/).|
92-
|[**com.slack.api:bolt-google-cloud-functions**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-google-cloud-functions)|This module offers a handy way to run Bolt apps on [Google Cloud Functions](https://cloud.google.com/functions).|
93-
|[**com.slack.api:bolt-micronaut**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-micronaut)|This is an adapter for [Micronaut](https://micronaut.io/) to run Bolt apps on top of it.|
94-
|[**com.slack.api:bolt-helidon**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-helidon)|This is an adapter for [Helidon SE](https://helidon.io/docs/latest/) to run Bolt apps on top of it.|
95-
|[**com.slack.api:bolt-http4k**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-http4k)|This is an adapter for [http4k](https://http4k.org/) to run Bolt apps on top of any of the multiple server backends that the library supports.|
96-
|[**com.slack.api:bolt-ktor**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-ktor)|This is an adapter for [Ktor](https://ktor.io/) to run Bolt apps on top of it.|
86+
| groupId:artifactId | Description |
87+
|------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
88+
| [**com.slack.api:bolt**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt) | Bolt is a framework that offers an abstraction layer to build Slack apps safely and quickly. The most commonly used Servlet environment is supported out-of-the-box. |
89+
| [**com.slack.api:bolt-socket-mode**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-socket-mode) | This module offers a handy way to run Bolt apps through [Socket Mode](https://api.slack.com/) connections. |
90+
| [**com.slack.api:bolt-jetty**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-jetty) | This module offers a handy way to run Bolt apps on the [Java EE compatible Jetty HTTP server (9.x)](https://www.eclipse.org/jetty/). |
91+
| [**com.slack.api:bolt-jakarta-jetty**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-jakarta-jetty) | This module offers a handy way to run Bolt apps on the [Jakarta EE compatible Jetty HTTP server](https://www.eclipse.org/jetty/). |
92+
| [**com.slack.api:bolt-aws-lambda**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-aws-lambda) | This module offers a handy way to run Bolt apps on AWS [API Gateway](https://aws.amazon.com/api-gateway/) + [Lambda](https://aws.amazon.com/lambda/). |
93+
| [**com.slack.api:bolt-google-cloud-functions**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-google-cloud-functions) | This module offers a handy way to run Bolt apps on [Google Cloud Functions](https://cloud.google.com/functions). |
94+
| [**com.slack.api:bolt-micronaut**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-micronaut) | This is an adapter for [Micronaut](https://micronaut.io/) to run Bolt apps on top of it. |
95+
| [**com.slack.api:bolt-helidon**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-helidon) | This is an adapter for [Helidon SE](https://helidon.io/docs/latest/) to run Bolt apps on top of it. |
96+
| [**com.slack.api:bolt-http4k**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-http4k) | This is an adapter for [http4k](https://http4k.org/) to run Bolt apps on top of any of the multiple server backends that the library supports. |
97+
| [**com.slack.api:bolt-ktor**](https://search.maven.org/search?q=g:com.slack.api%20AND%20a:bolt-ktor) | This is an adapter for [Ktor](https://ktor.io/) to run Bolt apps on top of it. |
9798

9899
#### Foundation Modules
99100

bolt-jakarta-jetty/pom.xml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>com.slack.api</groupId>
8+
<artifactId>slack-sdk-parent</artifactId>
9+
<version>1.17.2-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>bolt-jakarta-jetty</artifactId>
13+
<version>1.17.2-SNAPSHOT</version>
14+
<packaging>jar</packaging>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>com.slack.api</groupId>
19+
<artifactId>slack-api-model</artifactId>
20+
<version>${project.version}</version>
21+
</dependency>
22+
<dependency>
23+
<groupId>com.slack.api</groupId>
24+
<artifactId>slack-api-client</artifactId>
25+
<version>${project.version}</version>
26+
</dependency>
27+
<dependency>
28+
<groupId>com.slack.api</groupId>
29+
<artifactId>slack-app-backend</artifactId>
30+
<version>${project.version}</version>
31+
</dependency>
32+
<dependency>
33+
<groupId>com.slack.api</groupId>
34+
<artifactId>bolt</artifactId>
35+
<version>${project.version}</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>com.slack.api</groupId>
39+
<artifactId>bolt-jakarta-servlet</artifactId>
40+
<version>${project.version}</version>
41+
</dependency>
42+
43+
<dependency>
44+
<groupId>jakarta.servlet</groupId>
45+
<artifactId>jakarta.servlet-api</artifactId>
46+
<version>${jakarta.servlet-api.version}</version>
47+
<scope>compile</scope>
48+
</dependency>
49+
<dependency>
50+
<groupId>org.eclipse.jetty</groupId>
51+
<artifactId>jetty-servlet</artifactId>
52+
<version>${jakarta.jetty.version}</version>
53+
<scope>compile</scope>
54+
</dependency>
55+
<dependency>
56+
<groupId>org.eclipse.jetty</groupId>
57+
<artifactId>jetty-server</artifactId>
58+
<version>${jakarta.jetty.version}</version>
59+
<scope>compile</scope>
60+
</dependency>
61+
<dependency>
62+
<groupId>org.eclipse.jetty</groupId>
63+
<artifactId>jetty-webapp</artifactId>
64+
<version>${jakarta.jetty.version}</version>
65+
<scope>compile</scope>
66+
</dependency>
67+
</dependencies>
68+
69+
</project>
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package com.slack.api.bolt.jakarta_jetty;
2+
3+
import com.slack.api.bolt.App;
4+
import com.slack.api.bolt.WebEndpoint;
5+
import com.slack.api.bolt.handler.WebEndpointHandler;
6+
import com.slack.api.bolt.jakarta_servlet.SlackAppServlet;
7+
import com.slack.api.bolt.jakarta_servlet.SlackOAuthAppServlet;
8+
import com.slack.api.bolt.jakarta_servlet.WebEndpointServlet;
9+
import lombok.extern.slf4j.Slf4j;
10+
import org.eclipse.jetty.server.ConnectionFactory;
11+
import org.eclipse.jetty.server.Connector;
12+
import org.eclipse.jetty.server.HttpConnectionFactory;
13+
import org.eclipse.jetty.server.Server;
14+
import org.eclipse.jetty.server.handler.ErrorHandler;
15+
import org.eclipse.jetty.servlet.ServletContextHandler;
16+
import org.eclipse.jetty.servlet.ServletHolder;
17+
18+
import jakarta.servlet.http.HttpServletRequest;
19+
import java.io.IOException;
20+
import java.io.Writer;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
24+
/**
25+
* An HTTP server backed by Jetty HTTP Server that runs {@link App} apps.
26+
*
27+
* @see <a href="https://www.eclipse.org/jetty/">Jetty HTTP Server</a>
28+
*/
29+
@Slf4j
30+
public class SlackAppServer {
31+
32+
private final Server server;
33+
private final Map<String, App> pathToApp;
34+
private final boolean localDebug = System.getenv("SLACK_APP_LOCAL_DEBUG") != null;
35+
36+
// This is intentionally mutable to allow developers to register their own one
37+
private ErrorHandler errorHandler = new ErrorHandler() {
38+
@Override
39+
protected void writeErrorPage(
40+
HttpServletRequest request,
41+
Writer writer,
42+
int code,
43+
String message,
44+
boolean showStacks) throws IOException {
45+
if (localDebug) {
46+
super.writeErrorPage(request, writer, code, message, showStacks);
47+
} else {
48+
writer.write("{\"status\":\"" + code + "\"}");
49+
}
50+
}
51+
};
52+
53+
public SlackAppServer(App app) {
54+
this(app, "/slack/events", 3000);
55+
}
56+
57+
public SlackAppServer(App app, String path) {
58+
this(app, path, 3000);
59+
}
60+
61+
public SlackAppServer(App app, int port) {
62+
this(toApps(app, "/slack/events"), port);
63+
}
64+
65+
public SlackAppServer(App app, String path, int port) {
66+
this(toApps(app, path), port);
67+
}
68+
69+
public SlackAppServer(Map<String, App> pathToApp) {
70+
this(pathToApp, 3000);
71+
}
72+
73+
public SlackAppServer(Map<String, App> pathToApp, int port) {
74+
this.pathToApp = pathToApp;
75+
server = new Server(port);
76+
removeServerHeader(server);
77+
78+
ServletContextHandler handler = new ServletContextHandler();
79+
Map<String, App> addedOnes = new HashMap<>();
80+
for (Map.Entry<String, App> entry : this.pathToApp.entrySet()) {
81+
String appPath = entry.getKey();
82+
App theApp = entry.getValue();
83+
theApp.config().setAppPath(appPath);
84+
handler.addServlet(new ServletHolder(new SlackAppServlet(theApp)), appPath);
85+
86+
if (theApp.config().isOAuthInstallPathEnabled() || theApp.config().isOAuthStartEnabled()) {
87+
if (theApp.config().isDistributedApp()) {
88+
// start
89+
String installPath = appPath + theApp.config().getOauthInstallPath();
90+
App installPathApp = theApp.toOAuthInstallPathEnabledApp();
91+
handler.addServlet(new ServletHolder(new SlackOAuthAppServlet(installPathApp)), installPath);
92+
addedOnes.put(installPath, installPathApp);
93+
} else {
94+
log.warn("The app is not ready for handling your Slack App installation URL. Make sure if you set all the necessary values in AppConfig.");
95+
}
96+
}
97+
98+
if (theApp.config().isOAuthRedirectUriPathEnabled() || theApp.config().isOAuthCallbackEnabled()) {
99+
if (theApp.config().isDistributedApp()) {
100+
// callback
101+
String redirectUriPath = appPath + theApp.config().getOauthRedirectUriPath();
102+
App oAuthCallbackApp = theApp.toOAuthRedirectUriPathEnabledApp();
103+
handler.addServlet(new ServletHolder(new SlackOAuthAppServlet(oAuthCallbackApp)), redirectUriPath);
104+
addedOnes.put(redirectUriPath, oAuthCallbackApp);
105+
} else {
106+
log.warn("The app is not ready for handling OAuth callback requests. Make sure if you set all the necessary values in AppConfig.");
107+
}
108+
}
109+
110+
// Register additional web endpoints
111+
if (theApp.getWebEndpointHandlers() != null && theApp.getWebEndpointHandlers().size() > 0) {
112+
for (Map.Entry<WebEndpoint, WebEndpointHandler> ee : theApp.getWebEndpointHandlers().entrySet()) {
113+
WebEndpoint endpoint = ee.getKey();
114+
WebEndpointHandler endpointHandler = ee.getValue();
115+
ServletHolder servletHolder = new ServletHolder(
116+
new WebEndpointServlet(endpoint, endpointHandler, theApp.config()));
117+
handler.addServlet(servletHolder, endpoint.getPath());
118+
}
119+
}
120+
}
121+
pathToApp.putAll(addedOnes);
122+
server.setHandler(handler);
123+
server.setErrorHandler(errorHandler);
124+
}
125+
126+
public void start() throws Exception {
127+
for (App app : pathToApp.values()) {
128+
app.start();
129+
}
130+
server.start();
131+
log.info("⚡️ Bolt app is running!");
132+
server.join();
133+
}
134+
135+
public void stop() throws Exception {
136+
for (App app : pathToApp.values()) {
137+
app.stop();
138+
}
139+
log.info("⚡️ Your Bolt app has stopped...");
140+
server.stop();
141+
}
142+
143+
public ErrorHandler getErrorHandler() {
144+
return errorHandler;
145+
}
146+
147+
public void setErrorHandler(ErrorHandler errorHandler) {
148+
this.errorHandler = errorHandler;
149+
}
150+
151+
// ----------------------------------------------------
152+
// internal methods
153+
// ----------------------------------------------------
154+
155+
private static Map<String, App> toApps(App app, String path) {
156+
Map<String, App> apps = new HashMap<>();
157+
apps.put(path, app);
158+
return apps;
159+
}
160+
161+
private static void removeServerHeader(Server server) {
162+
// https://stackoverflow.com/a/15675075/840108
163+
for (Connector y : server.getConnectors()) {
164+
for (ConnectionFactory x : y.getConnectionFactories()) {
165+
if (x instanceof HttpConnectionFactory) {
166+
((HttpConnectionFactory) x).getHttpConfiguration().setSendServerVersion(false);
167+
}
168+
}
169+
}
170+
}
171+
172+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* Jetty HTTP server for Bolt apps.
3+
*/
4+
package com.slack.api.bolt.jakarta_jetty;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package samples;
2+
3+
import com.slack.api.bolt.App;
4+
import com.slack.api.bolt.jakarta_jetty.SlackAppServer;
5+
import com.slack.api.model.event.AppMentionEvent;
6+
import com.slack.api.model.event.MessageBotEvent;
7+
import com.slack.api.model.event.MessageDeletedEvent;
8+
import com.slack.api.model.event.MessageEvent;
9+
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
13+
public class EventsApp {
14+
15+
public static void main(String[] args) throws Exception {
16+
App app = new App();
17+
18+
app.event(AppMentionEvent.class, (event, ctx) -> {
19+
ctx.logger.info("new mention by a user - {}", event);
20+
ctx.say("Hi there!");
21+
return ctx.ack();
22+
});
23+
app.event(MessageEvent.class, (event, ctx) -> {
24+
ctx.logger.info("new message by a user - {}", event);
25+
ctx.say("Hi hi!");
26+
return ctx.ack();
27+
});
28+
app.event(MessageBotEvent.class, (event, ctx) -> {
29+
ctx.logger.info("bot message - {}", event);
30+
return ctx.ack();
31+
});
32+
app.event(MessageDeletedEvent.class, (event, ctx) -> {
33+
ctx.logger.info("message deleted - {}", event);
34+
return ctx.ack();
35+
});
36+
37+
Map<String, App> apps = new HashMap<>();
38+
apps.put("/slack/events", app);
39+
apps.put("/slack/oauth/", new App().asOAuthApp(true));
40+
SlackAppServer server = new SlackAppServer(apps);
41+
server.start();
42+
}
43+
44+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<configuration>
2+
<appender name="default" class="ch.qos.logback.core.FileAppender">
3+
<file>bolt-jetty/logs/console.log</file>
4+
<encoder>
5+
<pattern>%date %level [%thread] %logger{64} %msg%n</pattern>
6+
</encoder>
7+
</appender>
8+
9+
<logger name="org.eclipse.jetty" level="warn"/>
10+
<root level="debug">
11+
<appender-ref ref="default"/>
12+
</root>
13+
14+
</configuration>

0 commit comments

Comments
 (0)