-
Notifications
You must be signed in to change notification settings - Fork 701
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: WebHookUtil classes using the jakarta namespace (#2484)
* feat: WebHookUtil classes using the jakarta namespace Fixes #2260 Copied the implementation with the jakarta.servlet namespace using the same methology as googleapis/google-oauth-java-client#1115. - Replaced javax with jakarta and declared the dependency with provided scope. - Keeping the classes as `Beta` annotated. - No tests exist for the classes. - Added `@since 2.6.0` as the last release was 2.5.1. * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
- Loading branch information
1 parent
3ba13b4
commit e928cfa
Showing
5 changed files
with
329 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
...e/api/client/googleapis/extensions/servlet/notifications/jakarta/NotificationServlet.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* | ||
* Copyright 2013 Google Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
* in compliance with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License | ||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | ||
* or implied. See the License for the specific language governing permissions and limitations under | ||
* the License. | ||
*/ | ||
|
||
package com.google.api.client.googleapis.extensions.servlet.notifications.jakarta; | ||
|
||
import com.google.api.client.googleapis.notifications.StoredChannel; | ||
import com.google.api.client.util.Beta; | ||
import com.google.api.client.util.store.DataStore; | ||
import com.google.api.client.util.store.DataStoreFactory; | ||
import com.google.api.client.util.store.MemoryDataStoreFactory; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServlet; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
|
||
/** | ||
* {@link Beta} <br> | ||
* Thread-safe Webhook Servlet to receive notifications using the {@code jakarta.servlet} namespace. | ||
* | ||
* <p>In order to use this servlet you should create a class inheriting from {@link | ||
* NotificationServlet} and register the servlet in your web.xml. | ||
* | ||
* <p>It is a simple wrapper around {@link WebhookUtils#processWebhookNotification}, so if you you | ||
* may alternatively call that method instead from your {@link HttpServlet#doPost} with no loss of | ||
* functionality. <b>Example usage:</b> | ||
* | ||
* <pre>{@code | ||
* public class MyNotificationServlet extends NotificationServlet { | ||
* | ||
* private static final long serialVersionUID = 1L; | ||
* | ||
* public MyNotificationServlet() throws IOException { | ||
* super(new SomeDataStoreFactory()); | ||
* } | ||
* } | ||
* }</pre> | ||
* | ||
* <b>Sample web.xml setup:</b> | ||
* | ||
* <pre>{@code | ||
* {@literal <}servlet{@literal >} | ||
* {@literal <}servlet-name{@literal >}MyNotificationServlet{@literal <}/servlet-name{@literal >} | ||
* {@literal <}servlet-class{@literal >} | ||
* com.mypackage.MyNotificationServlet | ||
* {@literal <}/servlet-class{@literal >} | ||
* {@literal <}/servlet{@literal >} | ||
* {@literal <}servlet-mapping{@literal >} | ||
* {@literal <}servlet-name{@literal >}MyNotificationServlet{@literal <}/servlet-name{@literal >} | ||
* {@literal <}url-pattern{@literal >}/notifications{@literal <}/url-pattern{@literal >} | ||
* {@literal <}/servlet-mapping{@literal >} | ||
* }</pre> | ||
* | ||
* <p>WARNING: by default it uses {@link MemoryDataStoreFactory#getDefaultInstance()} which means it | ||
* will NOT persist the notification channels when the servlet process dies, so it is a BAD CHOICE | ||
* for a production application. But it is a convenient choice when testing locally, in which case | ||
* you don't need to override it, and can simply reference it directly in your web.xml file. For | ||
* example: | ||
* | ||
* <pre>{@code | ||
* {@literal <}servlet{@literal >} | ||
* {@literal <}servlet-name{@literal >}NotificationServlet{@literal <}/servlet-name{@literal >} | ||
* {@literal <}servlet-class{@literal >} | ||
* com.google.api.client.googleapis.extensions.servlet.notificationsNotificationServlet | ||
* {@literal <}/servlet-class{@literal >} | ||
* {@literal <}/servlet{@literal >} | ||
* {@literal <}servlet-mapping{@literal >} | ||
* {@literal <}servlet-name{@literal >}NotificationServlet{@literal <}/servlet-name{@literal >} | ||
* {@literal <}url-pattern{@literal >}/notifications{@literal <}/url-pattern{@literal >} | ||
* {@literal <}/servlet-mapping{@literal >} | ||
* }</pre> | ||
* | ||
* @since 2.6.0 | ||
*/ | ||
@Beta | ||
public class NotificationServlet extends HttpServlet { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
/** Notification channel data store. */ | ||
private final transient DataStore<StoredChannel> channelDataStore; | ||
|
||
/** | ||
* Constructor to be used for testing and demo purposes that uses {@link | ||
* MemoryDataStoreFactory#getDefaultInstance()} which means it will NOT persist the notification | ||
* channels when the servlet process dies, so it is a bad choice for a production application. | ||
*/ | ||
public NotificationServlet() throws IOException { | ||
this(MemoryDataStoreFactory.getDefaultInstance()); | ||
} | ||
|
||
/** | ||
* Constructor which uses {@link StoredChannel#getDefaultDataStore(DataStoreFactory)} on the given | ||
* data store factory, which is the normal use case. | ||
* | ||
* @param dataStoreFactory data store factory | ||
*/ | ||
protected NotificationServlet(DataStoreFactory dataStoreFactory) throws IOException { | ||
this(StoredChannel.getDefaultDataStore(dataStoreFactory)); | ||
} | ||
|
||
/** | ||
* Constructor that allows a specific notification data store to be specified. | ||
* | ||
* @param channelDataStore notification channel data store | ||
*/ | ||
protected NotificationServlet(DataStore<StoredChannel> channelDataStore) { | ||
this.channelDataStore = channelDataStore; | ||
} | ||
|
||
@Override | ||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) | ||
throws ServletException, IOException { | ||
WebhookUtils.processWebhookNotification(req, resp, channelDataStore); | ||
} | ||
} |
166 changes: 166 additions & 0 deletions
166
...m/google/api/client/googleapis/extensions/servlet/notifications/jakarta/WebhookUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
/* | ||
* Copyright 2013 Google Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
* in compliance with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License | ||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | ||
* or implied. See the License for the specific language governing permissions and limitations under | ||
* the License. | ||
*/ | ||
|
||
package com.google.api.client.googleapis.extensions.servlet.notifications.jakarta; | ||
|
||
import com.google.api.client.googleapis.extensions.servlet.notifications.WebhookHeaders; | ||
import com.google.api.client.googleapis.notifications.StoredChannel; | ||
import com.google.api.client.googleapis.notifications.UnparsedNotification; | ||
import com.google.api.client.googleapis.notifications.UnparsedNotificationCallback; | ||
import com.google.api.client.util.Beta; | ||
import com.google.api.client.util.LoggingInputStream; | ||
import com.google.api.client.util.Preconditions; | ||
import com.google.api.client.util.StringUtils; | ||
import com.google.api.client.util.store.DataStore; | ||
import com.google.api.client.util.store.DataStoreFactory; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServlet; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.Enumeration; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
/** | ||
* {@link Beta} <br> | ||
* Utilities for Webhook notifications using the {@code jakarta.servlet} namespace. | ||
* | ||
* @since 2.6.0 | ||
*/ | ||
@Beta | ||
public final class WebhookUtils { | ||
|
||
static final Logger LOGGER = Logger.getLogger(WebhookUtils.class.getName()); | ||
|
||
/** Webhook notification channel type to use in the watch request. */ | ||
public static final String TYPE = "web_hook"; | ||
|
||
/** | ||
* Utility method to process the webhook notification from {@link HttpServlet#doPost} by finding | ||
* the notification channel in the given data store factory. | ||
* | ||
* <p>It is a wrapper around {@link #processWebhookNotification(HttpServletRequest, | ||
* HttpServletResponse, DataStore)} that uses the data store from {@link | ||
* StoredChannel#getDefaultDataStore(DataStoreFactory)}. | ||
* | ||
* @param req an {@link HttpServletRequest} object that contains the request the client has made | ||
* of the servlet | ||
* @param resp an {@link HttpServletResponse} object that contains the response the servlet sends | ||
* to the client | ||
* @param dataStoreFactory data store factory | ||
* @exception IOException if an input or output error is detected when the servlet handles the | ||
* request | ||
* @exception ServletException if the request for the POST could not be handled | ||
*/ | ||
public static void processWebhookNotification( | ||
HttpServletRequest req, HttpServletResponse resp, DataStoreFactory dataStoreFactory) | ||
throws ServletException, IOException { | ||
processWebhookNotification(req, resp, StoredChannel.getDefaultDataStore(dataStoreFactory)); | ||
} | ||
|
||
/** | ||
* Utility method to process the webhook notification from {@link HttpServlet#doPost}. | ||
* | ||
* <p>The {@link HttpServletRequest#getInputStream()} is closed in a finally block inside this | ||
* method. If it is not detected to be a webhook notification, an {@link | ||
* HttpServletResponse#SC_BAD_REQUEST} error will be displayed. If the notification channel is | ||
* found in the given notification channel data store, it will call {@link | ||
* UnparsedNotificationCallback#onNotification} for the registered notification callback method. | ||
* | ||
* @param req an {@link HttpServletRequest} object that contains the request the client has made | ||
* of the servlet | ||
* @param resp an {@link HttpServletResponse} object that contains the response the servlet sends | ||
* to the client | ||
* @param channelDataStore notification channel data store | ||
* @exception IOException if an input or output error is detected when the servlet handles the | ||
* request | ||
* @exception ServletException if the request for the POST could not be handled | ||
*/ | ||
public static void processWebhookNotification( | ||
HttpServletRequest req, HttpServletResponse resp, DataStore<StoredChannel> channelDataStore) | ||
throws ServletException, IOException { | ||
Preconditions.checkArgument("POST".equals(req.getMethod())); | ||
InputStream contentStream = req.getInputStream(); | ||
try { | ||
// log headers | ||
if (LOGGER.isLoggable(Level.CONFIG)) { | ||
StringBuilder builder = new StringBuilder(); | ||
Enumeration<?> e = req.getHeaderNames(); | ||
if (e != null) { | ||
while (e.hasMoreElements()) { | ||
Object nameObj = e.nextElement(); | ||
if (nameObj instanceof String) { | ||
String name = (String) nameObj; | ||
Enumeration<?> ev = req.getHeaders(name); | ||
if (ev != null) { | ||
while (ev.hasMoreElements()) { | ||
builder | ||
.append(name) | ||
.append(": ") | ||
.append(ev.nextElement()) | ||
.append(StringUtils.LINE_SEPARATOR); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
LOGGER.config(builder.toString()); | ||
contentStream = new LoggingInputStream(contentStream, LOGGER, Level.CONFIG, 0x4000); | ||
// TODO(yanivi): allow to override logging content limit | ||
} | ||
// parse the relevant headers, and create a notification | ||
Long messageNumber; | ||
try { | ||
messageNumber = Long.valueOf(req.getHeader(WebhookHeaders.MESSAGE_NUMBER)); | ||
} catch (NumberFormatException e) { | ||
messageNumber = null; | ||
} | ||
String resourceState = req.getHeader(WebhookHeaders.RESOURCE_STATE); | ||
String resourceId = req.getHeader(WebhookHeaders.RESOURCE_ID); | ||
String resourceUri = req.getHeader(WebhookHeaders.RESOURCE_URI); | ||
String channelId = req.getHeader(WebhookHeaders.CHANNEL_ID); | ||
String channelExpiration = req.getHeader(WebhookHeaders.CHANNEL_EXPIRATION); | ||
String channelToken = req.getHeader(WebhookHeaders.CHANNEL_TOKEN); | ||
String changed = req.getHeader(WebhookHeaders.CHANGED); | ||
if (messageNumber == null | ||
|| resourceState == null | ||
|| resourceId == null | ||
|| resourceUri == null | ||
|| channelId == null) { | ||
resp.sendError( | ||
HttpServletResponse.SC_BAD_REQUEST, | ||
"Notification did not contain all required information."); | ||
return; | ||
} | ||
UnparsedNotification notification = | ||
new UnparsedNotification(messageNumber, resourceState, resourceId, resourceUri, channelId) | ||
.setChannelExpiration(channelExpiration) | ||
.setChannelToken(channelToken) | ||
.setChanged(changed) | ||
.setContentType(req.getContentType()) | ||
.setContentStream(contentStream); | ||
// check if we know about the channel, hand over the notification to the notification callback | ||
StoredChannel storedChannel = channelDataStore.get(notification.getChannelId()); | ||
if (storedChannel != null) { | ||
storedChannel.getNotificationCallback().onNotification(storedChannel, notification); | ||
} | ||
} finally { | ||
contentStream.close(); | ||
} | ||
} | ||
|
||
private WebhookUtils() {} | ||
} |
23 changes: 23 additions & 0 deletions
23
...m/google/api/client/googleapis/extensions/servlet/notifications/jakarta/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright 2024 Google Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except | ||
* in compliance with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License | ||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express | ||
* or implied. See the License for the specific language governing permissions and limitations under | ||
* the License. | ||
*/ | ||
|
||
/** | ||
* {@link com.google.api.client.util.Beta} <br> | ||
* Support for subscribing to topics and receiving notifications on servlet-based platforms using | ||
* {@code jakarta.servlet} namespace. | ||
* | ||
* @since 2.6.0 | ||
*/ | ||
@com.google.api.client.util.Beta | ||
package com.google.api.client.googleapis.extensions.servlet.notifications.jakarta; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters