Cómo escribir una calculadora simple cliente-servidor (JavaFX + EJB + WildFly)

Entonces, digamos que queremos implementar una aplicación cliente-servidor donde se generarán los datos necesarios en el lado del cliente, y en el lado del servidor, el cálculo se realizará y se devolverá al cliente como resultado. Si tenemos en cuenta una calculadora simple (hagámoslo aún más fácil, 4 operadores, operandos sin fracciones y trabajamos de acuerdo con el esquema [operando1] [operador] [operando2] [resultado]) y, digamos, implementarlo en algún tipo de lenguaje de programación (lenguaje de programación ), por ejemplo, Java, utilizando el servidor de aplicaciones (por ejemplo, WildFly / JBoss) + cliente (puede usar JavaFX), puede hacerlo de la siguiente manera:

La misma opción se puede resolver usando RMI (Invocación de método remoto) sin un servidor, cliente GUI y EJB en la consola, pero no consideraremos esta opción, pero procederemos a una implementación más interesante.

1. Necesitaremos los siguientes ingredientes:
1.1. JDK
1.2. IDE (con soporte Java EE),
1.3. WildFly (u otro servidor de aplicaciones Java),
1.4. SceneBuilder (por conveniencia y creación rápida de GUI).

Para conectar el cliente al servidor, usaremos JNDI (un servicio de nomenclatura y directorio) usando EJB (un marco para construir lógica de negocios).

2. Cree una implementación del lado del servidor:
2.1. La interfaz remota a través de la cual se producirá la comunicación entre los clientes y el servidor (use la anotación remota : el componente EJB usará 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. Una clase que implementa esta interfaz (utilizamos la anotación @Stateless - un componente de sesión sin estado).

 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. Agregue nuestra clase de excepción, que señalará la división por cero o el formato incorrecto.

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

2.4. Usando el IDE, creamos un archivo ear (Enterprise Archive), iniciamos el servidor (es posible con un puerto estándar), lo implementamos y, si no se notaron errores, la parte del servidor está terminada.

El registro del servidor puede ser así:

  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. Creamos la implementación de la parte del cliente:
3.1. En SceneBuilder, dibuje el siguiente diseño de calculadora (main.fxml), por ahora no ajustaremos 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. Conectamos el controlador de clase al formulario fxml, para desbloquear la GUI (formulario fijo) mientras esperamos los datos del servidor, agregamos un nuevo hilo al botón "=":

Vista de código
 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. Agregue la lógica de la calculadora más simple:

 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. Agregue la biblioteca del cliente (en el caso de WildFly es jboss-client.jar), ejecute la GUI.

Como se puede ver en los códigos adjuntos, el usuario escribe dígitos en el búfer para operandos, selecciona el operador, cuando se presiona el botón "=", se realiza una conexión al servidor (a través del servicio JNDI), la inscripción en la pantalla "ESPERANDO ..." indicará que el servidor está esperando una respuesta.

También observamos un par de excepciones: NumberFormatException (capturado cuando el usuario ingresa datos en el cliente) y ArithmeticException (capturado cuando se divide por cero en el servidor).

Source: https://habr.com/ru/post/477436/


All Articles