将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,一经查实,立即删除!

相关文章

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

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

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…

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

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

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;只列出关键代码&#…

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;适…

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

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

前端总结·基础篇·CSS(二)视觉

前端总结系列 前端总结基础篇CSS&#xff08;一&#xff09;布局前端总结基础篇CSS&#xff08;二&#xff09;视觉前端总结基础篇CSS&#xff08;三&#xff09;补充前端总结基础篇JS&#xff08;一&#xff09;原型、原型链、构造函数和字符串&#xff08;String&#xff09;…

【kindle笔记】之 《浪潮之巅》- 2018-1-

《浪潮之巅》 这本书推荐自最爱的政治课老师。 政治课老师张巍老师。我会一直记得你的。 以这样的身份来到这个学校&#xff0c;他人的质疑&#xff0c;自己的忐忑&#xff0c;老板的不公。犹犹豫豫谨小慎微地前进。 第一次听到这样的话是从您口中&#xff1a; 在座的诸位&…

Secure CRT 自动记录日志log配置

SecureCRT8.0的下载地址下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1i5q09qH 密码&#xff1a;4pa2 配置自动log操作如下&#xff1a; 1.options ---> Session Options 2. 设置log 文件属性 点击 日志 &#xff0c;在选项框中 Log file name中填入路径和…

禁用mysql的sleep函数_MySQL的sleep函数的特殊特现象

MySQL中的系统函数sleep&#xff0c;实际应用的场景不多&#xff0c;一般用来做实验测试&#xff0c;昨天在测试的时候&#xff0c;意外发现sleep函数的一个特殊现象。如果在查询语句中使用sleep函数&#xff0c;那么休眠的时间跟返回的记录有关。如下测试所示&#xff1a;mysq…

amoeba mysql读写分离_Mysql 实现读写分离的详细教程(amoeba)

Mysql 实现读写分离的详细教程(amoeba)发布时间&#xff1a;2018-08-17作者&#xff1a;laosun阅读(2220)继上篇文章&#xff0c;mysql实现主从配置之后的读写分离实现方式&#xff0c;文章地址。amoeba是盛大架构师陈思儒独立完成&#xff0c;除此之外还有很多中间件&#xff…

十道海量数据处理面试题与十个方法大总结

1. 给定a、b两个文件&#xff0c;各存放50亿个url&#xff0c;每个url各占64字节&#xff0c;内存限制是4G&#xff0c;让你找出a、b文件共同的url&#xff1f; 方案1&#xff1a;可以估计每个文件安的大小为50G64320G&#xff0c;远远大于内存限制的4G。所以不可能将其完全加载…

Spring集成文件轮询和测试

我最近实施了一个小项目&#xff0c;在该项目中&#xff0c;我们必须轮询文件夹中的新文件&#xff0c;然后在文件内容上触发服务流。 Spring Integration非常适合此要求&#xff0c;因为它带有一个通道适配器 &#xff0c;该适配器可以扫描文件夹中的新文件&#xff0c;然后通…

Spark参数配置总结

转载于:https://www.cnblogs.com/lz3018/p/8128017.html

eclipse mysql生成实体类_Eclipse实现数据库反向生成实体类(pojo)-------(插件安装和实现步骤的说明)...

一、插件安装1.下载插件&#xff1a;http://jaist.dl.sourceforge.net/sourceforge/jboss/HibernateTools-3.2.4.Beta1-R200810311334.zip2.解压压缩包分别将其中的features和plugins放到Eclipse安装目录下对应的这2个文件里&#xff0c;重启Eclipse使其生效3.随便建个project&…