将JacpFX客户端与JSR 356 WebSockets一起使用

JSR 356 WebSockets是即将发布的JEE 7版本中令人兴奋的新功能之一,并且在其参考实现中包括Server-和Client API。 这使其非常适合在客户端与JavaFX集成。 JacpFX是JavaFX之上的RCP框架,它使用基于消息的方法与组件进行交互。 这种基于消息的方法使集成WebSocket-ClientEndpoints以及将传入消息传递到JavaFX / JacpFX应用程序变得容易。

下面的文章将向您展示如何在GlassFish 4上创建一个简单的Websocket-SeverEndpoint以及如何从JacpFX客户端联系该端点。 示例场景非常简单:服务器端点可以创建与Twitter的连接,它从客户端获取查询消息,并将Twitter结果广播到所有已连接的用户
ClientEndpoints。 该示例将基于GlassFish b85和JacpFX 1.2。 (maven)示例项目可以在这里下载

第1部分:创建服务器端点

示例项目是一个简单的JEE 7 maven项目。 该Web项目包含一个充当WebSocket-ServerEndpoint的POJO。 它接收查询请求,并将结果广播到所有连接的客户端。 第二个POJO是一个无状态Bean,它接收查询消息,执行twitter搜索并将结果返回到Server-Endpoint。 为了避免处理文本或二进制消息,我们创建了两个POJO,它们充当WebSocket消息的编码器/解码器。 让我们从TwitterRepositoryBean开始,这是一个简单的无状态bean:

@Stateless(mappedName = "TwitterRepositoryBean")public class TwitterRepositoryBean {public TwitterResult getTwitterDataByQuery(Query query) {return parser.fromJson(getFeedData(query.getQuery()), TwitterResult.class);}private String getFeedData(String input) {final String searchURL = "http://search.twitter.com/search.json?q=" + input + "&rpp=5&include_entities=true" +"&with_twitter_user_id=true&result_type=mixed";final URL twitter = new URL(searchURL);…return "";}}

接下来,我们创建一个WebSocket-ServerEndpoint。 创建端点的最简单方法是编写一个POJO并使用@ServerEndpoint(“ path”)对其进行注释。 端点可以具有以下生命周期注释:@ OnOpen,@ OnClose,@ OnError和一个或多个@OnMessage。

请注意,每种本机消息类型只允许使用一个@OnMessage。 这些是文本,二进制和傍。 这是什么意思? 您可以有两种这样的方法: @OnMessage login(Login lg);@OnMessage message(Message m) 。 但是一个必须以文本形式传输,另一个必须以二进制形式传输,否则部署时会出现异常。

ServerEndpoint将如下所示:

@ServerEndpoint(value = "/twitter")public class TwitterEndpoint {@Injectprivate TwitterRepositoryBean twitterRepo;@OnMessagepublic void handleChatMessage(Query query, Session session) {TwitterResult result =    twitterRepo.getTwitterDataByQuery(query);broadcastMessage(result,session);}private void broadcastMessage(TwitterResult result, Session session) {for (Session s : session.getOpenSessions()) {s.getBasicRemote().sendObject(result);}}}

现在我们创建了ServerEndpoint,问题是如何使用QueryTwitterResult类的类型进行处理,因为本机消息格式为二进制和文本。 解决方案是:消息“编码器/解码器”。 因此,我们需要一个将二进制消息转换为Query解码器,以及一个将TwitterResult编码为二进制的编码器。

public class QueryDecoder implements Decoder.Binary<Query> {public Query decode(ByteBuffer byteBuffer) throws DecodeException {return (Query) SerializationUtils.deserialize(byteBuffer.array());}public boolean willDecode(ByteBuffer byteBuffer) {Object message = SerializationUtils.deserialize(byteBuffer.array());if (message == null) return false;return message instanceof Query;}}

和编码器:

public class TwitterResultEncoder implements Encoder.Binary<TwitterResult> {public ByteBuffer encode(TwitterResult message) throws EncodeException {return ByteBuffer.wrap(SerializationUtils.serialize(message));}}

为了使编码器/解码器可用于端点,我们扩展@ServerEndpoint注释,如下所示:

@ServerEndpoint(value = "/twitter", decoders = {QueryDecoder.class},encoders = {TwitterResultEncoder.class})

因此,现在我们有一个完整的ServerEndpoint示例,您可以将其部署在任何符合JEE 7的应用服务器上。

第2部分:创建JacpFX客户端和ClientEndpoint

从JacpFX开始的最简单方法是使用提供的Maven原型。 它生成一个示例客户端,其中包括JacpFX的每个有趣方面。 因此,您可以立即使用工作台,两个透视图(fxml和JavaFX),两个UI组件和两个非UI组件。 因此,我们只需使用它并通过WebSocket-Endpoints对其进行扩展。

如果您有> Java7u6和maven,则只需键入:

mvn archetype:generate -DarchetypeGroupId=org.jacp -DarchetypeArtifactId=JacpFX-quickstart-archetype -DarchetypeVersion=1.2 -DarchetypeRepository=http://developer.ahcp.de/nexus/content/repositories/jacp

创建一个JacpFX项目。

要获取WebSocket-Client API的依赖关系,您需要在pom.xml中添加以下依赖关系:

<dependency><groupId>org.glassfish.tyrus</groupId><artifactId>tyrus-client</artifactId><version>1.0-rc1</version><scope>compile</scope></dependency><dependency><groupId>org.glassfish.tyrus</groupId><artifactId>tyrus-container-grizzly</artifactId><version>1.0-rc1</version><scope>compile</scope></dependency>

并添加以下存储库:

<repository><id>java.net-promoted</id><url>https://maven.java.net/content/groups/promoted/</url></repository>

现在的基本思想是,我们使用创建的有状态组件并将其用作WebSocket- ClientEndpoint。 每次收到新的TwitterReult ,我们都会将其传递到JacpFX消息总线,并将其委托给一个UI组件,该组件将结果呈现在表中。 另一方面,我们将TextField的输入委托给有状态组件,该组件将查询请求发送到ServerEndpoint。 因此,如下更改有状态组件:

id003

类级别的注释@ClientEndpoint将此组件标记为WebSocket-Endpoint,而@CallbackComponent包含此无状态组件的JacpFX元数据。 @OnStart init(..)方法包含用于连接到WebSocket-ServerEndpoint并将有状态组件的实例作为ClientEndpoint传递的代码。 @OnStart是JacpFX生命周期注释,将在激活组件时执行。 当组件从UI收到Query消息时,将执行“handleAction”方法。 在这里,我们称为“sendQuery”并使用WebSocket-Session将Query对象发送到服务器端点。

当服务器执行Query并从Twitter接收结果时,他TwitterResult广播到所有连接的客户端,因此@OnMessage onTwitterMessage(…)方法将在客户端执行。 在这里,我们调用组件的actionListener并将结果传递给ID为“ id001”的组件,以呈现结果。 像之前的ServerEndpoint一样,我们需要一个“编码器/解码器”来处理消息类型“ QueryTwitterResult ”。 因此,以相同的方式创建编码器/解码器,然后像这样在ClientEndpoint上注册它们:

@ClientEndpoint(decoders = {TwitterResultDecoder.class}, encoders = {QueryEncoder.class})

第3部分:更改示例客户端以显示Query-和TableView

最后一步是将示例JacpFX客户端中的UI组件更改为具有查询视图和表视图。 您基本上可以自由地使用输入字段和表来创建一个组件以呈现结果。 更好的方法是为此创建单独的组件。 在开始之前,您可以删除PerspectiveTwo.javaComponentFXMLRight.javaComponentFXMLBottom.java 。 还要删除resources/main.xml的引用,并将componentTop的引用添加到PerspectiveOne:

<bean id="perspectiveOne" class="org.jacp.client.perspectives.PerspectiveOne"><property name="subcomponents"><list><ref bean="componentLeft" /><ref bean="componentTop" /><ref bean="statefulCallback" /><ref bean="statelessCallback" /></list></property></bean>

第一个JacpFX透视图(perspectiveOne)通过FXML声明其UI,并充当两个组件的容器。 它已经是一个SplitPane ,我们只需更改代码即可垂直拆分视图。 因此,打开resources/fxml/perspectiveOne.fxml并在<SplitPane>元素上添加属性orientation="VERTICAL" ,并将dividerPositions更改为0.30。 现在打开PerspectiveOne.java并将组件的注册目标名称更改为:

perspectiveLayout.registerTargetLayoutComponent("QueryView",this.gridPaneLeft);perspectiveLayout.registerTargetLayoutComponent("TableView",this.gridPaneRight);

我们要做的是为我们的组件设置新的目标布局。 组件现在可以注册到这些目标布局之一,然后将在其中进行渲染。 这种注册机制使您可以在透视图中定义任何复杂的UI结构,并为组件定义渲染点。 现在,我们更改要在“QueryView”目标中呈现的ComponentTop 。 为此,我们只需像这样更改@Component的defaultExecutionTarget属性的值即可:

@Component(defaultExecutionTarget = "QueryView", id = "id006", name = "componentTop", active = true, resourceBundleLocation = "bundles.languageBundle", localeID = "en_US")

这个组件已经包含一个TextField和一个Button ,因此我们只需要更改Button的EventHandler即可传递TextFiled的值。

private EventHandler<Event> getEventHandler() {return new EventHandler<Event>() {@Overridepublic void handle(final Event arg0) {getActionListener("id01.id003",textField.getText()).performAction(arg0);}};}

getActionListener(“id01.id003”…只是在透视图“ id01”中定义了带有“ id003”的组件(有状态组件),作为此消息的目标。接下来,我们将ComponentLeft.java更改为TableView 。我们将默认的executionTarget更改为“TableView”还更改了createUI()方法以显示TableView并更新了postHandleAction来处理TwitterResults并将它们传递给表,最终的解决方案如下所示:

@Component(defaultExecutionTarget = " TableView ", id = "id001", name = "componentLeft", active = true, resourceBundleLocation = "bundles.languageBundle", localeID = "en_US")public class ComponentLeft extends AFXComponent {private AnchorPane pane;private ObservableList<Tweet> tweets = FXCollections.observableArrayList();@Override/*** The handleAction method always runs outside the main application thread. You can create new nodes, execute long running tasks but you are not allowed to manipulate existing nodes here.*/public Node handleAction(final IAction<Event, Object> action) {// runs in worker threadif (action.getLastMessage().equals(MessageUtil.INIT)) {return this.createUI();}return null;}@Override/*** The postHandleAction method runs always in the main application thread.*/public Node postHandleAction(final Node arg0,final IAction<Event, Object> action) {// runs in FX application threadif (action.getLastMessage().equals(MessageUtil.INIT)) {this.pane = (AnchorPane) arg0;} else if (action.getLastMessage() instanceof TwitterResult) {tweets.clear();TwitterResult result = (TwitterResult) action.getLastMessage();if (!result.getResults().isEmpty()) {tweets.addAll(result.getResults());Collections.sort(tweets);}}return this.pane;}/*** create the UI on first call** @return*/private Node createUI() {final AnchorPane anchor = AnchorPaneBuilder.create().styleClass("roundedAnchorPaneFX").build();TableView table = new TableView();table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);table.getColumns().addAll(createColumns());table.setItems(tweets);AnchorPane.setTopAnchor(table, 25.0);AnchorPane.setRightAnchor(table, 25.0);AnchorPane.setLeftAnchor(table, 25.0);anchor.getChildren().addAll(table);GridPane.setHgrow(anchor, Priority.ALWAYS);GridPane.setVgrow(anchor, Priority.ALWAYS);return anchor;}private List<TableColumn> createColumns() {…return Arrays.asList(imageView, nameView, messageView);}

JacpFX UI组件具有定义的生命周期:每条消息首先通过handleAction(..)传递,然后通过postHandleAction(…)方法postHandleAction(…)handleAction(..)始终在工作线程中执行,因此您可以执行任何复杂且耗时的计算,而不会阻塞UI。 在这里,您可以自由创建新的UI节点并返回它们。 但是,您不能更改任何现有节点,因为您不在JavaFX Application Thread上。 然后,返回的Node将被传递到在应用程序线程上运行的postHandleAction(…) 。 您可以在此处看到完整的生命周期:

postHandleAction(…)我们检查TwitterResult消息,并将Twitter条目添加到Table 。 现在我们完成了,您可以在GlassFish 4实例上部署ServerEndpoint并运行该应用程序。 如果启动许多实例,则TwitterResult将传递给所有连接的客户端。 您可以在项目Wiki的此处找到JacpFX的完整文档。 有关JSR 356的更多信息,请访问项目页面和Arun Gupta的博客。

资源资源

  • https://blogs.oracle.com/arungupta/
  • https://java.net/projects/tyrus
  • https://code.google.com/p/jacp/wiki/文档
  • http://www.javacodegeeks.com/2012/03/building-rich-clients-with-jacpfx-and.html

参考: 通过我们的W4G合作伙伴 Andy Moncsek 将JacpFX客户端与JSR 356 WebSocket 一起使用 。

翻译自: https://www.javacodegeeks.com/2013/04/using-jacpfx-clients-with-jsr-356-websockets.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/368413.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nagios check_mysql uptime_nagios使用check_mysql监控mysql

如果没有check_mysql插件&#xff0c;需要安装Mysql数据库1、建立专用数据库&#xff1a; [rootsvr3 ~]#mysql -u root -pEnter password:Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 51910Server version: 5.5.3-m3-log Source di…

《精通Spring4.X企业应用开发实战》读后感第七章(AOP概念)

转载于:https://www.cnblogs.com/Michael2397/p/8068486.html

XHTML与HTML的区别

XHTML的语法较为严谨&#xff0c;拥有一定的规则&#xff0c;如果不遵循规则的话容易出错。但也不必太过担心&#xff0c;因为XHTML的规则并不太难&#xff0c;它和HTML4.01标准没有太多的不同。 需要注意的是以下几点&#xff1a; 1.XHTML标签必须被正确的关闭&#xff0c;即…

EC2上的ElasticSearch不到60秒

好奇地看到所有ElasticSearch轮奸是关于什么的&#xff1f; 想在没有大量肘部油脂的情况下看到它吗&#xff1f; 然后&#xff0c;朋友&#xff0c; 别再犹豫了-不到60秒&#xff0c;我将向您展示如何在AWS AMI上安装ElasticSearch 。 您首先需要一个AWS账户以及一个SSH密钥对…

Material使用04 MdCardModule和MdButtonModule综合运用

设计需求&#xff1a;设计一个登陆页面 1 模块导入 1.1 将MdCardModule和MdButtonModule模块导入到共享模块中 import { NgModule } from angular/core; import { CommonModule } from angular/common; import { MdSidenavModule, MdToolbarModule,MdIconModule,MdButtonModule…

mysql子分区多少层_MYSQL子分区修剪

我有一个MYSQL表与分区的年份和子分区的月份。MYSQL子分区修剪CREATE TABLE ptable (id INT NOT NULL AUTO_INCREMENT,name varchar(100),purchased DATETIME NOT NULL,PRIMARY KEY (id, purchased))PARTITION BY RANGE(YEAR(purchased))SUBPARTITION BY HASH(MONTH(purchased)…

65. Valid Number

Validate if a given string is numeric. Some examples:"0" > true" 0.1 " > true"abc" > false"1 a" > false"2e10" > true 判断字符串是否代表了有效数字。 这道题有点坑&#xff0c;情况比较多…… 1 cl…

python实现bp神经网络对csv文件进行数据预测

参考资源&#xff1a; sklearn库 bp神经网络[从原理到代码一篇搞定]&#xff08;2&#xff09;_sklearn 神经网络-CSDN博客 十分钟上手sklearn&#xff1a;安装&#xff0c;获取数据&#xff0c;数据预处理 - 知乎 (zhihu.com) 一个实例讲解如何使用BP神经网络(附代码) - 知…

html实现 页面禁止右键 禁止复制 禁止图片拖动 禁止复制和剪切

众所周知&#xff0c;一般的屏蔽的方法是用JS来编写的脚本&#xff0c;但是也可以直接通过修改网页属性的方法来屏蔽右键 禁止复制。 禁止右键 οncοntextmenu"return false" 禁止复制和剪切&#xff1a; οncοpy"return false;" oncut"return fa…

Spring MVC:表单处理卷。 4 –单选按钮

在软件开发领域&#xff0c;单选按钮是图形用户界面的重要组成部分。 Spring MVC标签库为此表单元素提供了特殊标签。 更确切地说&#xff0c;有两个标签用于此目的&#xff1a;单选按钮&#xff0c;单选按钮。 两者对于特定任务都是有用的。 如您所知&#xff0c;我将在帖子中…

点击时候确定某个元素 js_某空气质量监测平台 JS反爬

目标&#xff1a;中国空气质量在线监测分析平台|城市分析 参考CSDN中文章&#xff0c;记录一下学习过程通过切换城市&#xff0c;页面数据是通过 Ajax 加载的&#xff0c;数据接口&#xff1a;https://www.aqistudy.cn/apinew/aqistudyapi.php请求的POST Data、返回的数据都被加…

HTML 部分常用属性、组成属性|...超链接、路径、锚点、列表、滚动、URL编码、表格、表单、GET和POST

URL地址 就是我们所说的网址&#xff1a;www.jd.com 浏览器内核&#xff0c;渲染引擎 Ie内核&#xff1a;triteent 谷歌/欧鹏&#xff1a;blink 火狐&#xff1a;gecko 苹果&#xff1a;webkit 渲染引擎是出现兼容性的根本问题 -html概念:hyper Text Markup Languag…

Spring @Value取值为null或@Autowired注入失败

Value 用于注入.properties文件中定义的内容 Autowired 用于装配bean 用法都很简单&#xff0c;很直接&#xff0c;但是稍不注意就会出错。下面就来说说我遇到的问题。 前两天在项目中遇到了一个问题&#xff0c;大致描述就是我写了如下一个类&#xff08;只列出关键代码&#…

2个域名重定向到https域名

配置实例&#xff1a; [rootiZbp17q09o7e8pgg9dybd7Z conf.d]# cat company.confserver { listen 80; server_name www.yu*******dao.com www.bj******kd.com; if ($host ! www.yu********dao.com) { rewrite ^/(.*)$ http://www.yu**********dao.com/$1 permanent; } return …

mysql 部门表_MySQL高级

本文大纲环境win10-64MySQL Community Server 5.7.1mysqld –version可查看版本官方文档SQL执行顺序手写顺序我们可以将手写SQL时遵循的格式归结如下&#xff1a;select distinct from join on wheregroup byhavingorder bylimit <offset>,<rows>distinct&…

使用Apache Camel发布/订阅模式

发布/订阅是一种简单的消息传递模式&#xff0c;其中&#xff0c;发布者将消息发送到某个频道&#xff0c;而无需知道谁将接收消息。 然后&#xff0c;通道负责将消息的副本传递给每个订户。 此消息传递模型允许创建松耦合和可伸缩的系统。 这是一种非常常见的消息传递模式&am…

移动端(H5)弹框组件--简单--实用--不依赖jQuery

俗话说的好&#xff0c;框架是服务与大家的&#xff0c;包含的功能比较多&#xff0c;代码多。在现在追求速度的年代。应该根据自己的需求去封装自己所需要的组件。 下边就给大家介绍一下自己封装的一个小弹框组件&#xff0c;不依赖与jQuery&#xff0c;代码少&#xff0c;适…

IDEA界面

Alt数字&#xff1a;出来你需要的界面转载于:https://www.cnblogs.com/chanaichao/p/9214327.html

在单节点和多节点上的Hadoop设置

我们将描述在单节点和多节点上的Hadoop设置。 将详细描述Hadoop环境的设置和配置。 首先&#xff0c;您需要下载以下软件&#xff08;rpm&#xff09;。 Java JDK RPM Apache Hadoop 0.20.204.0 RPM A&#xff09;单节点系统Hadoop设置 1&#xff09;在Red Hat或CentOS 5系统…

c++ for循环 流程图_python 零基础必知--条件控制与循环语句

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理本文章来自腾讯云 作者&#xff1a;somenzz如果没有 if 语句和循环语句&#xff0c;请问你怎么编程&#xff1f;Python 中的条件控制和循环语句都非常简单&…