| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.wicket.protocol.ws.api; |
| |
| import java.util.Map; |
| import java.util.Set; |
| |
| import jakarta.servlet.SessionTrackingMode; |
| import jakarta.servlet.http.Cookie; |
| import jakarta.servlet.http.HttpServletRequest; |
| import org.apache.wicket.Component; |
| import org.apache.wicket.behavior.Behavior; |
| import org.apache.wicket.markup.head.IHeaderResponse; |
| import org.apache.wicket.markup.head.JavaScriptHeaderItem; |
| import org.apache.wicket.markup.head.OnDomReadyHeaderItem; |
| import org.apache.wicket.protocol.http.WebApplication; |
| import org.apache.wicket.protocol.ws.WebSocketSettings; |
| import org.apache.wicket.util.cookies.CookieUtils; |
| import org.apache.wicket.util.lang.Args; |
| import org.apache.wicket.util.lang.Generics; |
| import org.apache.wicket.util.string.Strings; |
| import org.apache.wicket.util.template.PackageTextTemplate; |
| |
| /** |
| * A behavior that contributes {@link WicketWebSocketJQueryResourceReference} |
| */ |
| public class BaseWebSocketBehavior extends Behavior |
| { |
| private final String resourceName; |
| private final String connectionToken; |
| |
| /** |
| * Constructor. |
| * |
| * Contributes WebSocket initialization code that will |
| * work with {@link org.apache.wicket.protocol.ws.api.WebSocketBehavior} |
| */ |
| protected BaseWebSocketBehavior() |
| { |
| this.resourceName = null; |
| this.connectionToken = null; |
| } |
| |
| /** |
| * Constructor. |
| * |
| * Contributes WebSocket initialization code that will |
| * work with {@link org.apache.wicket.protocol.ws.api.WebSocketResource} |
| * |
| * To use WebSocketResource the application have to setup the |
| * resource as a shared one in its {@link org.apache.wicket.Application#init()} |
| * method: |
| * <code><pre> |
| * getSharedResources().add(resourceName, new MyWebSocketResource()) |
| * </pre></code> |
| * |
| * @param resourceName |
| * the name of the shared {@link org.apache.wicket.protocol.ws.api.WebSocketResource} |
| */ |
| public BaseWebSocketBehavior(String resourceName) |
| { |
| this(resourceName, null); |
| } |
| |
| /** |
| * Constructor. |
| * |
| * Contributes WebSocket initialization code that will |
| * work with {@link org.apache.wicket.protocol.ws.api.WebSocketResource} |
| * |
| * To use WebSocketResource the application have to setup the |
| * resource as a shared one in its {@link org.apache.wicket.Application#init()} |
| * method: |
| * <code><pre> |
| * getSharedResources().add(resourceName, new MyWebSocketResource()) |
| * </pre></code> |
| * |
| * @param resourceName |
| * the name of the shared {@link org.apache.wicket.protocol.ws.api.WebSocketResource} |
| * @param connectionToken |
| * an optional token to support connections to the same resource from multiple browser tabs |
| */ |
| public BaseWebSocketBehavior(String resourceName, String connectionToken) |
| { |
| this.resourceName = Args.notEmpty(resourceName, "resourceName"); |
| this.connectionToken = connectionToken; |
| } |
| |
| @Override |
| public void renderHead(Component component, IHeaderResponse response) |
| { |
| super.renderHead(component, response); |
| |
| response.render(JavaScriptHeaderItem.forReference(WicketWebSocketJQueryResourceReference.get())); |
| |
| Map<String, Object> parameters = getParameters(component); |
| String webSocketSetupScript = getWebSocketSetupScript(parameters); |
| |
| response.render(OnDomReadyHeaderItem.forScript(webSocketSetupScript)); |
| } |
| |
| protected String getWebSocketSetupScript(Map<String, Object> parameters) { |
| PackageTextTemplate webSocketSetupTemplate = |
| new PackageTextTemplate(WicketWebSocketJQueryResourceReference.class, |
| "res/js/wicket-websocket-setup.js.tmpl"); |
| |
| return webSocketSetupTemplate.asString(parameters); |
| } |
| |
| private Map<String, Object> getParameters(Component component) { |
| Map<String, Object> variables = Generics.newHashMap(); |
| |
| // set falsy JS values for the non-used parameter |
| if (Strings.isEmpty(resourceName)) |
| { |
| int pageId = component.getPage().getPageId(); |
| variables.put("pageId", pageId); |
| variables.put("resourceName", ""); |
| variables.put("connectionToken", ""); |
| } |
| else |
| { |
| variables.put("resourceName", resourceName); |
| variables.put("connectionToken", connectionToken); |
| variables.put("pageId", false); |
| } |
| |
| WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(component.getApplication()); |
| |
| CharSequence baseUrl = getBaseUrl(webSocketSettings); |
| Args.notNull(baseUrl, "baseUrl"); |
| variables.put("baseUrl", baseUrl); |
| |
| Integer port = getPort(webSocketSettings); |
| variables.put("port", port); |
| Integer securePort = getSecurePort(webSocketSettings); |
| variables.put("securePort", securePort); |
| |
| CharSequence contextPath = getContextPath(webSocketSettings); |
| Args.notNull(contextPath, "contextPath"); |
| variables.put("contextPath", contextPath); |
| |
| // preserve the application name for JSR356 based impl |
| variables.put("applicationName", component.getApplication().getName()); |
| |
| CharSequence filterPrefix = getFilterPrefix(webSocketSettings); |
| Args.notNull(filterPrefix, "filterPrefix"); |
| variables.put("filterPrefix", filterPrefix); |
| |
| final CharSequence sessionId = getSessionId(component); |
| variables.put("sessionId", sessionId); |
| return variables; |
| } |
| |
| protected Integer getPort(WebSocketSettings webSocketSettings) |
| { |
| return webSocketSettings.getPort(); |
| } |
| |
| protected Integer getSecurePort(WebSocketSettings webSocketSettings) |
| { |
| return webSocketSettings.getSecurePort(); |
| } |
| |
| protected CharSequence getFilterPrefix(final WebSocketSettings webSocketSettings) { |
| return webSocketSettings.getFilterPrefix(); |
| } |
| |
| protected CharSequence getContextPath(final WebSocketSettings webSocketSettings) { |
| return webSocketSettings.getContextPath(); |
| } |
| |
| protected CharSequence getBaseUrl(final WebSocketSettings webSocketSettings) { |
| return webSocketSettings.getBaseUrl(); |
| } |
| |
| /** |
| * @param component |
| * The component this behavior is bound to |
| * @return The http session id if it is tracked in the url, otherwise empty string |
| */ |
| protected CharSequence getSessionId(final Component component) |
| { |
| String sessionId = ""; |
| final WebApplication application = (WebApplication) component.getApplication(); |
| final Set<SessionTrackingMode> effectiveSessionTrackingModes = application.getServletContext().getEffectiveSessionTrackingModes(); |
| Object containerRequest = component.getRequest().getContainerRequest(); |
| if (effectiveSessionTrackingModes.size() == 1 && SessionTrackingMode.URL.equals(effectiveSessionTrackingModes.iterator().next())) |
| { |
| sessionId = component.getSession().getId(); |
| } |
| else if (containerRequest instanceof HttpServletRequest) |
| { |
| CookieUtils cookieUtils = new CookieUtils(); |
| final String jsessionCookieName = cookieUtils.getSessionIdCookieName(application); |
| final Cookie jsessionid = cookieUtils.getCookie(jsessionCookieName); |
| HttpServletRequest httpServletRequest = (HttpServletRequest) containerRequest; |
| if (jsessionid == null || httpServletRequest.isRequestedSessionIdValid() == false) |
| { |
| sessionId = component.getSession().getId(); |
| } |
| } |
| return sessionId; |
| } |
| |
| @Override |
| public boolean getStatelessHint(Component component) |
| { |
| return false; |
| } |
| } |