All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.faces.component.UIWebsocket Maven / Gradle / Ivy

Go to download

Jakarta Server Faces defines an MVC framework for building user interfaces for web applications, including UI components, state management, event handing, input validation, page navigation, and support for internationalization and accessibility.

There is a newer version: 3.1.0.SP02
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package javax.faces.component;

import static java.util.Collections.unmodifiableList;
import static javax.faces.push.PushContext.ENABLE_WEBSOCKET_ENDPOINT_PARAM_NAME;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.regex.Pattern;

import javax.el.ValueExpression;
import javax.faces.component.behavior.ClientBehaviorHolder;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.push.Push;
import javax.faces.push.PushContext;
import javax.websocket.CloseReason.CloseCodes;

/**
 * 

* The <f:websocket> tag opens an one-way (server to client) websocket based push connection in client * side which can be reached from server side via {@link PushContext} interface injected in any CDI/container managed * artifact via @{@link Push} annotation. *

* *

* By default, the rendererType property must be set to "javax.faces.Websocket". * This value can be changed by calling the setRendererType() method. *

* *

* For detailed usage instructions, see @{@link Push} javadoc. *

* * @see Push * @since 2.3 */ public class UIWebsocket extends UIComponentBase implements ClientBehaviorHolder { // ---------------------------------------------------------------------------------------------- Manifest Constants /** *

* The standard component type for this component. *

*/ public static final String COMPONENT_TYPE = "javax.faces.Websocket"; /** *

* The standard component family for this component. *

*/ public static final String COMPONENT_FAMILY = "javax.faces.Script"; /** *

* Properties that are tracked by state saving. *

*/ enum PropertyKeys { channel, scope, user, onopen, onmessage, onclose, connected; } private static final Pattern PATTERN_CHANNEL_NAME = Pattern.compile("[\\w.-]+"); private static final String ERROR_ENDPOINT_NOT_ENABLED = "f:websocket endpoint is not enabled." + " You need to set web.xml context param '" + ENABLE_WEBSOCKET_ENDPOINT_PARAM_NAME + "' with value 'true'."; private static final String ERROR_INVALID_CHANNEL = "f:websocket 'channel' attribute '%s' does not represent a valid channel name. It is required, it may not be an" + "Jakarta Expression Language expression and it may only contain alphanumeric characters, hyphens, underscores and periods."; private static final String ERROR_INVALID_USER = "f:websocket 'user' attribute '%s' does not represent a valid user identifier. It must implement Serializable and" + " preferably have low memory footprint. Suggestion: use #{request.remoteUser} or #{someLoggedInUser.id}."; // ---------------------------------------------------------------------------------------------------- Constructors /** *

* Create a new {@link UIWebsocket} instance with default property values. *

* * @throws IllegalStateException When Websocket endpoint is not enabled. */ public UIWebsocket() { ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext(); if (!Boolean.parseBoolean(externalContext.getInitParameter(ENABLE_WEBSOCKET_ENDPOINT_PARAM_NAME))) { throw new IllegalStateException(ERROR_ENDPOINT_NOT_ENABLED); } } // --------------------------------------------------------------------------------------------- UIComponent Methods /** *

* Returns {@link UIWebsocket#COMPONENT_FAMILY}. *

*/ @Override public String getFamily() { return COMPONENT_FAMILY; } /** *

* Set the {@link ValueExpression} used to calculate the value for the specified attribute or property name, if any. * If a {@link ValueExpression} is set for the channel or scope property, regardless of * the value, throw an illegal argument exception. If a {@link ValueExpression} is set for the user * property, and the non-null value is not an instance of Serializable, throw an illegal argument * exception. *

* * @throws IllegalArgumentException If name is one of id, parent, * channel or scope, or it name is user and the non-null value * is not an instance of Serializable. * @throws NullPointerException If name is null. */ @Override public void setValueExpression(String name, ValueExpression binding) { if (PropertyKeys.channel.toString().equals(name) || PropertyKeys.scope.toString().equals(name)) { throw new IllegalArgumentException(name); } if (PropertyKeys.user.toString().equals(name)) { Object user = binding.getValue(getFacesContext().getELContext()); if (user != null && !(user instanceof Serializable)) { throw new IllegalArgumentException(String.format(ERROR_INVALID_USER, user)); } } super.setValueExpression(name, binding); } // ------------------------------------------------------------------------------------ ClientBehaviorHolder Methods /** *

* Returns a non-null, empty, unmodifiable Collection which returns true on any * Collection#contains() invocation, indicating that all client behavior event names are acceptable. *

*/ @Override public Collection getEventNames() { return CONTAINS_EVERYTHING; } private static final Collection CONTAINS_EVERYTHING = unmodifiableList(new ArrayList() { private static final long serialVersionUID = 1L; @Override public boolean contains(Object object) { return true; } }); // ------------------------------------------------------------------------------------------------------ Properties /** * Returns the name of the websocket channel. * @return The name of the websocket channel. */ public String getChannel() { return (String) getStateHelper().eval(PropertyKeys.channel); } /** * Sets the name of the websocket channel. * It may not be an Jakarta Expression Language expression and it may only contain alphanumeric characters, hyphens, underscores and periods. * All open websockets on the same channel will receive the same push message from the server. * @param channel The name of the websocket channel. * @throws IllegalArgumentException When the value does not represent a valid channel name. */ public void setChannel(String channel) { if (channel == null || !PATTERN_CHANNEL_NAME.matcher(channel).matches()) { throw new IllegalArgumentException(String.format(ERROR_INVALID_CHANNEL, channel)); } getStateHelper().put(PropertyKeys.channel, channel); } /** * Returns the scope of the websocket channel. * @return The scope of the websocket channel. */ public String getScope() { return (String) getStateHelper().eval(PropertyKeys.scope); } /** * Sets the scope of the websocket channel. * It may not be an Jakarta Expression Language expression and allowed values are application, session and * view, case insensitive. When the value is application, then all channels with the same * name throughout the application will receive the same push message. When the value is session, then * only the channels with the same name in the current user session will receive the same push message. When the * value is view, then only the channel in the current view will receive the push message. The default * scope is application. When the user attribute is specified, then the default scope is * session. * @param scope The scope of the websocket channel. */ public void setScope(String scope) { getStateHelper().put(PropertyKeys.scope, scope); } /** * Returns the user identifier of the websocket channel. * @return The user identifier of the websocket channel. */ public Serializable getUser() { return (Serializable) getStateHelper().eval(PropertyKeys.user); } /** * Sets the user identifier of the websocket channel, so that user-targeted push messages can be sent. * All open websockets on the same channel and user will receive the same push message from the server. * It must implement Serializable and preferably have low memory footprint. * Suggestion: use #{request.remoteUser} or #{someLoggedInUser.id}. * @param user The user identifier of the websocket channel. */ public void setUser(Serializable user) { getStateHelper().put(PropertyKeys.user, user); } /** * Returns the JavaScript event handler function that is invoked when the websocket is opened. * @return The JavaScript event handler function that is invoked when the websocket is opened. */ public String getOnopen() { return (String) getStateHelper().eval(PropertyKeys.onopen); } /** * Sets the JavaScript event handler function that is invoked when the websocket is opened. * The function will be invoked with one argument: the channel name. * @param onopen The JavaScript event handler function that is invoked when the websocket is opened. */ public void setOnopen(String onopen) { getStateHelper().put(PropertyKeys.onopen, onopen); } /** * Returns the JavaScript event handler function that is invoked when a push message is received from the server. * @return The JavaScript event handler function that is invoked when a push message is received from the server. */ public String getOnmessage() { return (String) getStateHelper().eval(PropertyKeys.onmessage); } /** * Sets the JavaScript event handler function that is invoked when a push message is received from the server. The * function will be invoked with three arguments: the push message, the channel name and the raw MessageEvent itself. * @param onmessage The JavaScript event handler function that is invoked when a push message is received from the server. */ public void setOnmessage(String onmessage) { getStateHelper().put(PropertyKeys.onmessage, onmessage); } /** * Returns the JavaScript event handler function that is invoked when the websocket is closed. * @return The JavaScript event handler function that is invoked when the websocket is closed. */ public String getOnclose() { return (String) getStateHelper().eval(PropertyKeys.onclose); } /** * Sets the JavaScript event handler function that is invoked when the websocket is closed. * The function will be invoked with three arguments: the close reason code, the channel name and the raw * CloseEvent itself. Note that this will also be invoked on errors and that you can inspect the close * reason code if an error occurred and which one (i.e. when the code is not 1000). See also * RFC 6455 section 7.4.1 and {@link CloseCodes} API * for an elaborate list of all close codes. * @param onclose The JavaScript event handler function that is invoked when the websocket is closed. */ public void setOnclose(String onclose) { getStateHelper().put(PropertyKeys.onclose, onclose); } /** * Returns whether to (auto)connect the websocket or not. * @return Whether to (auto)connect the websocket or not. */ public boolean isConnected() { return (Boolean) getStateHelper().eval(PropertyKeys.connected, Boolean.TRUE); } /** * Sets whether to (auto)connect the websocket or not. Defaults to true. It's interpreted as a * JavaScript instruction whether to open or close the websocket push connection. Note that this attribute is * re-evaluated on every ajax request. You can also explicitly set it to false and then manually * control in JavaScript by OmniFaces.Push.open("channelName") and * OmniFaces.Push.close("channelName"). * @param connected Whether to (auto)connect the websocket or not. */ public void setConnected(boolean connected) { getStateHelper().put(PropertyKeys.connected, connected); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy