如何编写一个简单的客户端-服务器计算器(JavaFX + EJB + WildFly)

因此,假设我们要实现一个客户端-服务器应用程序,其中将在客户端生成必要​​的数据,然后将在服务器端进行计算并作为结果返回给客户端。 如果我们考虑一个简单的计算器(让它变得更简单,则有4个运算符,不带分数的操作数,并根据方案[operand1] [operator] [operand2] [result]进行工作),并且可以用某种编程语言(编程语言)来实现)(例如Java),使用应用程序服务器(例如WildFly / JBoss)+客户端(可以使用JavaFX),则可以通过以下方式进行操作:

使用RMI(远程方法调用)可以在控制台中没有服务器,GUI客户端和EJB的情况下解决相同的选项,但是我们将不考虑该选项,而是继续进行更有趣的实现。

1.我们将需要以下成分:
1.1。 JDK,
1.2。 IDE(具有Java EE支持),
1.3。 WildFly(或其他Java应用程序服务器),
1.4。 SceneBuilder(为了方便和快速创建GUI)。

要将客户端连接到服务器,我们将使用通过EJB(用于构建业务逻辑的框架)的JNDI(命名和目录服务)。

2.创建服务器端的实现:
2.1。 客户端和服务器之间通过其进行通信的远程接口(使用Remote注释-EJB组件将使用RMI)。

package com.calc.server; import javax.ejb.Remote; @Remote public interface CalcRemote { int add(int a, int b); int sub(int a, int b); int mul(int a, int b); int div(int a, int b) throws MyException; } 

2.2。 实现此接口的类(我们使用批注@Stateless-无状态会话组件)。

 package com.calc.server; import javax.ejb.Stateless; @Stateless(name = "CalcSessionEJB") public class CalcSessionBean implements CalcRemote { public CalcSessionBean() { } @Override public int add(int a, int b) { return a + b; } @Override public int sub(int a, int b) { return a - b; } @Override public int mul(int a, int b) { return a * b; } @Override public int div(int a, int b) throws MyException { try { return a / b; } catch (ArithmeticException ex) { throw new MyException("Divide by Zero!!!"); } } } 

2.3。 添加我们的异常类,该异常类将信号除以零或格式错误。

 package com.calc.server; public class MyException extends Exception { public MyException(String message) { super(message); } } 

2.4。 使用IDE,我们创建一个ear文件(企业归档),启动服务器(可以使用标准端口),然后将其部署到服务器,如果没有发现错误,则服务器部分完成。

服务器日志可以像这样:

  java:global/Calc_ear_exploded/ejb/CalcSessionEJB!com.calc.server.CalcRemote java:app/ejb/CalcSessionEJB!com.calc.server.CalcRemote java:module/CalcSessionEJB!com.calc.server.CalcRemote java:jboss/exported/Calc_ear_exploded/ejb/CalcSessionEJB!com.calc.server.CalcRemote ejb:Calc_ear_exploded/ejb/CalcSessionEJB!com.calc.server.CalcRemote java:global/Calc_ear_exploded/ejb/CalcSessionEJB java:app/ejb/CalcSessionEJB java:module/CalcSessionEJB 

3.我们创建客户端部分的实现:
3.1。 在SceneBuilder中,草绘以下计算器布局(main.fxml),我们还没有结束CSS:

 <?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="321.0" prefWidth="231.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="com.calc.client.impl2.controller.Controller"> <children> <Label disable="false" layoutX="24.0" layoutY="15.0" prefHeight="28.0" prefWidth="189.0" text="EJB Calculator" /> <TextField fx:id="displayTextField" layoutX="24.0" layoutY="43.0" prefHeight="56.0" prefWidth="182.0" /> <Button fx:id="num7Button" layoutX="24.0" layoutY="110.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="7" /> <Button fx:id="num8Button" layoutX="72.0" layoutY="110.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="8" /> <Button fx:id="num9Button" layoutX="119.0" layoutY="110.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="9" /> <Button fx:id="divButton" layoutX="166.0" layoutY="110.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="/" /> <Button fx:id="num4Button" layoutX="24.0" layoutY="161.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="4" /> <Button fx:id="num5Button" layoutX="72.0" layoutY="161.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="5" /> <Button fx:id="num6Button" layoutX="119.0" layoutY="161.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="6" /> <Button fx:id="mulButton" layoutX="166.0" layoutY="161.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="*" /> <Button fx:id="num1Button" layoutX="24.0" layoutY="209.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="1" /> <Button fx:id="num2Button" layoutX="72.0" layoutY="209.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="2" /> <Button fx:id="num3Button" layoutX="119.0" layoutY="209.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="3" /> <Button fx:id="subButton" layoutX="166.0" layoutY="209.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="-" /> <Button fx:id="num0Button" layoutX="24.0" layoutY="259.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="0" /> <Button fx:id="clrButton" layoutX="72.0" layoutY="259.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="C" /> <Button fx:id="resButton" layoutX="119.0" layoutY="259.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="=" /> <Button fx:id="addButton" layoutX="166.0" layoutY="259.0" mnemonicParsing="false" prefHeight="39.0" prefWidth="40.0" text="+" /> </children> </AnchorPane> 

3.2。 我们将类控制器连接到fxml表单,以在等待来自服务器的数据时解锁GUI(粘性表单),将新线程添加到“ =”按钮:

代码视图
 package com.calc.client.impl2.controller; import com.calc.server.CalcRemote; import com.calc.server.MyException; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.TextField; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import java.util.Properties; public class Controller implements Runnable { private Calculator calculator; @FXML private TextField displayTextField; @FXML private Button num1Button; @FXML private Button num2Button; @FXML private Button num3Button; @FXML private Button num4Button; @FXML private Button num5Button; @FXML private Button num6Button; @FXML private Button num7Button; @FXML private Button num8Button; @FXML private Button num9Button; @FXML private Button num0Button; @FXML private Button addButton; @FXML private Button subButton; @FXML private Button mulButton; @FXML private Button divButton; @FXML private Button clrButton; @FXML private Button resButton; @FXML private void initialize() { System.out.println("initialize()"); calculator = new Calculator(); displayTextField.setText("0"); num1Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("1"))); num2Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("2"))); num3Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("3"))); num4Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("4"))); num5Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("5"))); num6Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("6"))); num7Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("7"))); num8Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("8"))); num9Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("9"))); num0Button.setOnAction(event -> displayTextField.setText(calculator.addNumber("0"))); addButton.setOnAction(event -> { calculator.addOperator("+"); displayTextField.setText(""); }); subButton.setOnAction(event -> { calculator.addOperator("-"); displayTextField.setText(""); }); mulButton.setOnAction(event -> { calculator.addOperator("*"); displayTextField.setText(""); }); divButton.setOnAction(event -> { calculator.addOperator("/"); displayTextField.setText(""); }); resButton.setOnAction(event -> new Thread(this).start()); clrButton.setOnAction(event -> displayTextField.setText("")); } private void doRequest(String[] data) throws NamingException, MyException { Properties props = new Properties(); props.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory"); // props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); // props.put(Context.PROVIDER_URL, "remote+http://localhost:8080"); props.put(Context.PROVIDER_URL, "http-remoting://localhost:8080"); // props.put("jboss.naming.client.ejb.context", "true"); // props.put(Context.SECURITY_PRINCIPAL, "admin"); // props.put(Context.SECURITY_CREDENTIALS, "123"); Context ctx = new InitialContext(props); //System.out.println(ctx.getEnvironment()); CalcRemote calcRemote = (CalcRemote) ctx.lookup("Calc_ear_exploded/ejb/CalcSessionEJB!com.calc.server.CalcRemote"); //java:jboss/exported/Calc_ear_exploded/ejb/CalcSessionEJB!com.calc.server.CalcRemote String res = Integer.toString(process(calcRemote, data)); displayTextField.setText(res); System.out.println(res); } private int process(CalcRemote calcRemote, String[] resData) throws MyException { int operand1 = Integer.parseInt(resData[0]); int operand2 = Integer.parseInt(resData[1]); String operator = resData[2]; switch (operator) { case "+": return calcRemote.add(operand1, operand2); case "-": return calcRemote.sub(operand1, operand2); case "*": return calcRemote.mul(operand1, operand2); case "/": return calcRemote.div(operand1, operand2); } return 0; } @Override public void run() { displayTextField.setText("WAITING..."); try { doRequest(calculator.getResult()); } catch (NamingException ex) { ex.printStackTrace(); } catch (MyException ex) { displayTextField.setText(ex.getMessage()); } } } 



3.3。 添加最简单的计算器的逻辑:

 package com.calc.client.impl2.controller; import com.calc.server.MyException; public class Calculator { private String buffer = "", operator, operand1, operand2; private boolean isOperator = false; public String addNumber(String value) { buffer += value; if (!isOperator) { operand1 = buffer; } else { operand2 = buffer; } return buffer; } public void addOperator(String value) { operator = value; buffer = ""; isOperator = true; } public String[] getResult() throws MyException { isOperator = false; buffer = ""; int check; try { check = Integer.parseInt(operand1); check = Integer.parseInt(operand2); } catch (NumberFormatException ex) { throw new MyException("Wrong format!!!"); } return new String[]{operand1, operand2, operator}; } } 

3.5。 添加客户端库(在WildFly的情况下为jboss-client.jar),运行GUI。

从所附代码可以看出,用户在操作数缓冲区中键入数字,选择运算符,当按下``=''按钮时,将与服务器建立连接(通过JNDI服务),显示屏上的``WAITING ...''字样将指示服务器正在等待响应。

我们还注意到几个异常:NumberFormatException(当用户在客户端上输入数据时捕获)和ArithmeticException(当在服务器上被零除时捕获)。

Source: https://habr.com/ru/post/zh-CN477436/


All Articles