Встроенный кластер HornetQ Spring Boot не пересылает сообщения

Я пытаюсь создать статический кластер из двух приложений Spring Boot со встроенными серверами HornetQ. Одно приложение / сервер будет обрабатывать внешние события и генерировать сообщения для отправки в очередь сообщений. Другое приложение / сервер будет прослушивать очередь сообщений и обрабатывать входящие сообщения. Поскольку связь между двумя приложениями ненадежна, каждое из них будет использовать только локальные клиенты / клиенты inVM для создания / потребления сообщений на своем соответствующем сервере и полагаться на функциональные возможности кластеризации для пересылки сообщений в очередь на другом сервере в кластере.

Я использую HornetQConfigurationCustomizer для настройки встроенного сервера HornetQ, потому что по умолчанию он поставляется только с InVMConnectorFactory.

Я создал несколько примеров приложений, которые иллюстрируют эту настройку, в этом примере «ServerSend» относится к серверу, который будет создавать сообщения, а «ServerReceive» относится к серверу, который будет принимать сообщения.

pom.xml для обоих приложений содержит:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-hornetq</artifactId>
</dependency>
<dependency>
    <groupId>org.hornetq</groupId>
    <artifactId>hornetq-jms-server</artifactId>
</dependency>

DemoHornetqServerSendApplication:

@SpringBootApplication
@EnableScheduling
public class DemoHornetqServerSendApplication {
    @Autowired
    private JmsTemplate jmsTemplate;
    private @Value("${spring.hornetq.embedded.queues}") String testQueue;

    public static void main(String[] args) {
        SpringApplication.run(DemoHornetqServerSendApplication.class, args);
    }

    @Scheduled(fixedRate = 5000)
    private void sendMessage() {
        String message = "Timestamp from Server: " + System.currentTimeMillis();
        System.out.println("Sending message: " + message);
        jmsTemplate.convertAndSend(testQueue, message);
    }

    @Bean
    public HornetQConfigurationCustomizer hornetCustomizer() {
        return new HornetQConfigurationCustomizer() {

            @Override
            public void customize(Configuration configuration) {
                String serverSendConnectorName = "server-send-connector";
                String serverReceiveConnectorName = "server-receive-connector";

                Map<String, TransportConfiguration> connectorConf = configuration.getConnectorConfigurations();

                Map<String, Object> params = new HashMap<String, Object>();
                params.put(TransportConstants.HOST_PROP_NAME, "localhost");
                params.put(TransportConstants.PORT_PROP_NAME, "5445");
                TransportConfiguration tc = new TransportConfiguration(NettyConnectorFactory.class.getName(), params);
                connectorConf.put(serverSendConnectorName, tc);

                Set<TransportConfiguration> acceptors = configuration.getAcceptorConfigurations();
                tc = new TransportConfiguration(NettyAcceptorFactory.class.getName(), params);
                acceptors.add(tc);

                params = new HashMap<String, Object>();
                params.put(TransportConstants.HOST_PROP_NAME, "localhost");
                params.put(TransportConstants.PORT_PROP_NAME, "5446");
                tc = new TransportConfiguration(NettyConnectorFactory.class.getName(), params);
                connectorConf.put(serverReceiveConnectorName, tc);

                List<String> staticConnectors = new ArrayList<String>();
                staticConnectors.add(serverReceiveConnectorName);
                ClusterConnectionConfiguration conf = new ClusterConnectionConfiguration(
                        "my-cluster", // name
                        "jms", // address
                        serverSendConnectorName, // connector name
                        500, // retry interval
                        true, // duplicate detection
                        true, // forward when no consumers
                        1, // max hops
                        1000000, // confirmation window size
                        staticConnectors, 
                        true // allow direct connections only
                        );
                configuration.getClusterConfigurations().add(conf);

                AddressSettings setting = new AddressSettings();
                setting.setRedistributionDelay(0);
                configuration.getAddressesSettings().put("#", setting);
            }
        };
    }
}

application.properties (ServerSend):

spring.hornetq.mode=embedded
spring.hornetq.embedded.enabled=true
spring.hornetq.embedded.queues=jms.testqueue
spring.hornetq.embedded.cluster-password=password

DemoHornetqServerReceiveApplication:

@SpringBootApplication
@EnableJms
public class DemoHornetqServerReceiveApplication {
    @Autowired
    private JmsTemplate jmsTemplate;
    private @Value("${spring.hornetq.embedded.queues}") String testQueue;

    public static void main(String[] args) {
        SpringApplication.run(DemoHornetqServerReceiveApplication.class, args);
    }

    @JmsListener(destination="${spring.hornetq.embedded.queues}")
    public void receiveMessage(String message) {
        System.out.println("Received message: " + message);
    }

    @Bean
    public HornetQConfigurationCustomizer hornetCustomizer() {
        return new HornetQConfigurationCustomizer() {

            @Override
            public void customize(Configuration configuration) {
                String serverSendConnectorName = "server-send-connector";
                String serverReceiveConnectorName = "server-receive-connector";

                Map<String, TransportConfiguration> connectorConf = configuration.getConnectorConfigurations();

                Map<String, Object> params = new HashMap<String, Object>();
                params.put(TransportConstants.HOST_PROP_NAME, "localhost");
                params.put(TransportConstants.PORT_PROP_NAME, "5446");
                TransportConfiguration tc = new TransportConfiguration(NettyConnectorFactory.class.getName(), params);
                connectorConf.put(serverReceiveConnectorName, tc);

                Set<TransportConfiguration> acceptors = configuration.getAcceptorConfigurations();
                tc = new TransportConfiguration(NettyAcceptorFactory.class.getName(), params);
                acceptors.add(tc);

                params = new HashMap<String, Object>();
                params.put(TransportConstants.HOST_PROP_NAME, "localhost");
                params.put(TransportConstants.PORT_PROP_NAME, "5445");
                tc = new TransportConfiguration(NettyConnectorFactory.class.getName(), params);
                connectorConf.put(serverSendConnectorName, tc);

                List<String> staticConnectors = new ArrayList<String>();
                staticConnectors.add(serverSendConnectorName);
                ClusterConnectionConfiguration conf = new ClusterConnectionConfiguration(
                        "my-cluster", // name
                        "jms", // address
                        serverReceiveConnectorName, // connector name
                        500, // retry interval
                        true, // duplicate detection
                        true, // forward when no consumers
                        1, // max hops
                        1000000, // confirmation window size
                        staticConnectors, 
                        true // allow direct connections only
                        );
                configuration.getClusterConfigurations().add(conf);

                AddressSettings setting = new AddressSettings();
                setting.setRedistributionDelay(0);
                configuration.getAddressesSettings().put("#", setting);
            }
        };
    }
}

application.properties (ServerReceive):

spring.hornetq.mode=embedded
spring.hornetq.embedded.enabled=true
spring.hornetq.embedded.queues=jms.testqueue
spring.hornetq.embedded.cluster-password=password

После запуска обоих приложений вывод журнала показывает следующее:

ServerSend:

2015-04-09 11:11:58.471  INFO 7536 --- [           main] org.hornetq.core.server                  : HQ221000: live server is starting with configuration HornetQ Configuration (clustered=true,backup=false,sharedStore=true,journalDirectory=C:\Users\****\AppData\Local\Temp\hornetq-data/journal,bindingsDirectory=data/bindings,largeMessagesDirectory=data/largemessages,pagingDirectory=data/paging)  
2015-04-09 11:11:58.501  INFO 7536 --- [           main] org.hornetq.core.server                  : HQ221045: libaio is not available, switching the configuration into NIO  
2015-04-09 11:11:58.595  INFO 7536 --- [           main] org.hornetq.core.server                  : HQ221043: Adding protocol support CORE  
2015-04-09 11:11:58.720  INFO 7536 --- [           main] org.hornetq.core.server                  : HQ221003: trying to deploy queue jms.queue.jms.testqueue  
2015-04-09 11:11:59.568  INFO 7536 --- [           main] org.hornetq.core.server                  : HQ221020: Started Netty Acceptor version 4.0.13.Final localhost:5445  
2015-04-09 11:11:59.593  INFO 7536 --- [           main] org.hornetq.core.server                  : HQ221007: Server is now live  
2015-04-09 11:11:59.593  INFO 7536 --- [           main] org.hornetq.core.server                  : HQ221001: HornetQ Server version 2.4.5.FINAL (Wild Hornet, 124)   [c139929d-d90f-11e4-ba2e-e58abf5d6944] 

ServerReceive:

2015-04-09 11:12:04.401  INFO 4528 --- [           main] org.hornetq.core.server                  : HQ221000: live server is starting with configuration HornetQ Configuration (clustered=true,backup=false,sharedStore=true,journalDirectory=C:\Users\****\AppData\Local\Temp\hornetq-data/journal,bindingsDirectory=data/bindings,largeMessagesDirectory=data/largemessages,pagingDirectory=data/paging)  
2015-04-09 11:12:04.410  INFO 4528 --- [           main] org.hornetq.core.server                  : HQ221045: libaio is not available, switching the configuration into NIO  
2015-04-09 11:12:04.520  INFO 4528 --- [           main] org.hornetq.core.server                  : HQ221043: Adding protocol support CORE  
2015-04-09 11:12:04.629  INFO 4528 --- [           main] org.hornetq.core.server                  : HQ221003: trying to deploy queue jms.queue.jms.testqueue  
2015-04-09 11:12:05.545  INFO 4528 --- [           main] org.hornetq.core.server                  : HQ221020: Started Netty Acceptor version 4.0.13.Final localhost:5446  
2015-04-09 11:12:05.578  INFO 4528 --- [           main] org.hornetq.core.server                  : HQ221007: Server is now live  
2015-04-09 11:12:05.578  INFO 4528 --- [           main] org.hornetq.core.server                  : HQ221001: HornetQ Server version 2.4.5.FINAL (Wild Hornet, 124)   [c139929d-d90f-11e4-ba2e-e58abf5d6944] 

Я вижу clustered=true в обоих выходных данных, и это будет показывать false, если я удалю конфигурацию кластера из HornetQConfigurationCustomizer, поэтому это должно иметь какой-то эффект.

Теперь ServerSend показывает это в выводе консоли:

Sending message: Timestamp from Server: 1428574324910  
Sending message: Timestamp from Server: 1428574329899  
Sending message: Timestamp from Server: 1428574334904  

Однако ServerReceive ничего не показывает.

Похоже, что сообщения не пересылаются от ServerSend к ServerReceive.

Я провел еще несколько тестов, создав еще два приложения Spring Boot (ClientSend и ClientReceive), которые не имеют встроенный сервер HornetQ и вместо этого подключаются к «собственному» серверу.

pom.xml для обоих клиентских приложений содержит:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-hornetq</artifactId>
</dependency>

DemoHornetqClientSendApplication:

@SpringBootApplication
@EnableScheduling
public class DemoHornetqClientSendApplication {
    @Autowired
    private JmsTemplate jmsTemplate;
    private @Value("${queue}") String testQueue;

    public static void main(String[] args) {
        SpringApplication.run(DemoHornetqClientSendApplication.class, args);
    }

    @Scheduled(fixedRate = 5000)
    private void sendMessage() {
        String message = "Timestamp from Client: " + System.currentTimeMillis();
        System.out.println("Sending message: " + message);
        jmsTemplate.convertAndSend(testQueue, message);
    }
}

application.properties (ClientSend):

spring.hornetq.mode=native
spring.hornetq.host=localhost
spring.hornetq.port=5446

queue=jms.testqueue

DemoHornetqClientReceiveApplication:

@SpringBootApplication
@EnableJms
public class DemoHornetqClientReceiveApplication {
    @Autowired
    private JmsTemplate jmsTemplate;
    private @Value("${queue}") String testQueue;

    public static void main(String[] args) {
        SpringApplication.run(DemoHornetqClientReceiveApplication.class, args);
    }

    @JmsListener(destination="${queue}")
    public void receiveMessage(String message) {
        System.out.println("Received message: " + message);
    }
}

application.properties (ClientReceive):

spring.hornetq.mode=native
spring.hornetq.host=localhost
spring.hornetq.port=5445

queue=jms.testqueue

Теперь консоль показывает это:

Сервер

Received message: Timestamp from Client: 1428574966630  
Received message: Timestamp from Client: 1428574971600  
Received message: Timestamp from Client: 1428574976595  

ClientReceive:

Received message: Timestamp from Server: 1428574969436  
Received message: Timestamp from Server: 1428574974438  
Received message: Timestamp from Server: 1428574979446  

Если у меня ServerSend запущено какое-то время, а затем запущено ClientReceive, он также получает все сообщения, поставленные в очередь до этого момента, поэтому это показывает, что сообщения не просто куда-то исчезают и не потребляются откуда-то еще.

Для полноты картины я также указал ClientSend на ServerSend и ClientReceive на ServerReceive, чтобы увидеть, есть ли какие-то проблемы с кластеризацией и клиентами InVM, но опять же не было никаких сообщений, указывающих, что какое-либо сообщение было получено в ClientReceive или ServerReceive.

Таким образом, доставка сообщений от каждого встроенного брокера к напрямую подключенным внешним клиентам работает нормально, но сообщения между брокерами в кластере не пересылаются.

Итак, после всего этого возникает большой вопрос, что не так с настройкой, при которой сообщения не пересылаются внутри кластера?


person ci_    schedule 09.04.2015    source источник
comment
Вы узнали ответ на этот вопрос за последний месяц? Я ничего не нашел в Интернете (кроме этого сообщения) о связи Spring boot hornetq между двумя процессами ...   -  person Cristina_eGold    schedule 12.05.2015
comment
@ deepdownunder2222 пока что не повезло, мне удалось сгруппировать таким образом двух внешних брокеров, но даже это не сработало на 100%, как я ожидал, поэтому я припарковал это на данный момент. Вместо этого мне удалось сделать то, что мне нужно, с помощью ActiveMq, но он еще не прошел стадию прототипа. То, что я описал в этом вопросе, не кажется популярным занятием, особого интереса пока не было.   -  person ci_    schedule 12.05.2015
comment
Вы смотрели на установку моста вместо этого, кажется, что архитектура лучше соответствует тому, чего вы пытаетесь достичь: docs.jboss.org/hornetq/2.2.5.Final/user-manual/en/html/   -  person grahamrb    schedule 03.08.2016
comment
Я согласен с grahamrb, я использовал мост для этого точного сценария, где у нас есть 4 сервера, и 2 из 4 должны быть единственными серверами для приема сообщений определенного типа. Для этого сценария лучше подходят мосты.   -  person pczeus    schedule 19.12.2016
comment
forward-when-no-consumer = true будет проблемой. Вы должны оставить значение false. Вы не хотите, чтобы сообщения пересылались на узел в кластере, у которого нет потребителей, доступных для обработки сообщений - вот как сообщения «застревают». Кроме того, поскольку вы выполняете кластеризацию, помните, что исходный сервер ServerSend также является частью кластера. Следовательно, HornetQ может и будет оптимизировать и хранить сообщения в локальной виртуальной машине. Даже если это не так, ServerSend находится в кластере и будет разделять нагрузку / сообщения. forward-when-no-consumer = false помогает здесь.   -  person pczeus    schedule 19.12.2016


Ответы (3)


http://docs.jboss.org/hornetq/2.2.5.Final/user-manual/en/html/architecture.html#d0e595.

«Ядро HornetQ спроектировано как набор простых POJO, поэтому, если у вас есть приложение, которое требует функциональности обмена сообщениями внутри, но вы не хотите раскрывать его в качестве сервера HornetQ, вы можете напрямую создавать экземпляры и встраивать серверы HornetQ в свое собственное приложение».

Если вы встраиваете его, вы не выставляете его как сервер. У каждого из ваших контейнеров есть отдельный экземпляр. Это эквивалентно запуску двух экземпляров шершня и присвоению им одного и того же имени очереди. Один записывает в эту очередь в первом экземпляре, а другой слушает очередь во втором экземпляре.

Если вы хотите разделить свои приложения таким образом, вам нужно иметь единое место, которое действует как сервер. Наверное, вы хотите кластеризоваться. Кстати, это не относится к Hornet. Вы часто встретите этот образец.

person Joseph Spears    schedule 18.07.2015
comment
Меня этот ответ не убедил. 1. В цитируемых документах конкретно не говорится, что то, что я пытаюсь сделать, невозможно. 2. Я уже выставляю встроенный сервер для внешнего доступа, т.е. подключаюсь к нему с внешнего клиента, проблем с этим нет. 3. Да, кластеризация - это именно то, что я ищу, и это именно то, о чем идет речь, а также именно то, что не работает. - person ci_; 21.07.2015
comment
ci_ на самом деле кластеризация - это НЕ то, что вы ищете. Кластеры используются для обеспечения избыточности, а также для распределения нагрузки. Вы пытаетесь использовать один сервер для отправки напрямую другому серверу. Это можно сделать? да. Это цель кластеризации? Нет. - person pczeus; 19.12.2016

ФИЛЬТР

package it.unitn.disi.webdev.claudiovigliarolo;

import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebFilter(filterName = "AuthenticationFilter", urlPatterns = { "/*" })
public class AuthenticationFilter implements Filter {
    private ServletContext context;

    public void init(FilterConfig fConfig) throws ServletException {
        this.context = fConfig.getServletContext();
        this.context.log("AuthenticationFilter initialized");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        String uri = req.getRequestURI();
        HttpSession session = req.getSession(false);
        boolean isLoggedIn = session != null && session.getAttribute("username") != null;

        if (!isLoggedIn && !uri.endsWith("start.jsp")) {
            res.sendRedirect("start.jsp");

        } else {
            chain.doFilter(request, response);
        }
    }

    public void destroy() {
        // close any resources here
    }
}<filter-mapping><filter-name>AuthenticationFilter</filter-name><url-pattern>/*</url-pattern>
  </filter-mapping>




import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet(name = "GetItems", urlPatterns = { "/GetItems" })
public class GetItems extends HttpServlet {

    String dbURL = "jdbc:derby://localhost:1527/ExamDerbyDB";
    String user = "WEBENGINE";
    String password = "WEBENGINE";
    Connection conn = null;

    @Override
    public void init() {
        try {
            Class.forName("org.apache.derby.jdbc.ClientDriver");
            conn = DriverManager.getConnection(dbURL, user, password);
        } catch (ClassNotFoundException | SQLException ex) {
            ex.printStackTrace();

        }
    }

    @Override
    public void destroy() {
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException ex) {
            System.err.println("Database connection problem: can't close connection");

        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=UTF-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        StringBuilder ret = new StringBuilder();

        ArrayList<String> inserted = getAllItemsFromDB();

        String jsonResponse = getListJson(inserted);
        response.setContentType("application/json;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {

            out.println(jsonResponse);
        }
    }

    private ArrayList<String> getAllItemsFromDB() throws ServletException {
        ArrayList elements = new ArrayList();
        
        PreparedStatement stm = null;
        ResultSet result = null;
        
        try {
            String query = "SELECT USERNAME FROM USERS";
            stm = conn.prepareStatement(query);
            result = stm.executeQuery();
            while(result.next()) {
                String string = result.getString(1); 
                elements.add(string);
            }
            stm.close();
            result.close();

        } catch (SQLException ex) {
            System.err.println("Database connection problem");
        } finally {
            try {
                if(stm != null) stm.close();
                if(result != null) result.close();
            } catch (SQLException ex) {
               System.err.println("Database connection problem: can't close statement/result");
            }
        
        return elements;
    }

    public String getListJson(ArrayList<String> list) {
        if (list.size() == 0)
            return null;
        StringBuilder ret = new StringBuilder("[");
        String prefix = "";
        for (int i = 0; i < list.size(); i++) {
            StringBuilder sb = new StringBuilder();
            ret.append(prefix);
            prefix = ",";
            ret.append(sb.append("{\"message\":\"").append(list.get(i)).append("\"}").toString());
        }
        ret.append("]");
        return ret.toString();
    }
}

class.java

public class MessageList {
    protected final LinkedList<Message> list;

    public MessageList() {
        this.list = new LinkedList<>();
    }

    public void addMessage(Message m) {
        this.list.add(m);
    }

    public void deleteMessage(String message_id) {
        for (Message a : list) {
            if (a.message_id.equals(message_id)) {
                list.remove(a);
            }
        }
    }

    public Message getMessage(String message_id) {
        for (Message a : list) {
            if (a.message_id.equals(message_id)) {
                return a;
            }
        }
        return null;

    }

    public void addLike(String message_id) {
        for (Message a : list) {
            if (a.message_id.equals(message_id)) {
                a.isLiked++;
            }
        }
    }

    StringBuilder ret = new StringBuilder("[");
    String prefix = "";for(
    int i = 0;i<list.size();i++)
    {
        Message m = list.get(i);
        ret.append(prefix);
        prefix = ",";
        ret.append(m.toJson());
    }ret.append("]");

    return ret.toString();
    }

    public String getListJson()
    {
        if(list.size() == 0)
        {
            return null;
        }
        
        StringBuilder ret = new StringBuilder("[");
        String prefix = "";
        for(int i=0; i<list.size(); i++) {
            StringBuilder sb = new StringBuilder();
            ret.append(prefix);
            prefix = ",";
            ret.append(sb.append("{\"message\":\"").append(list.get(i)).append("\"}").toString());
        }
        ret.append("]");
        
        return ret.toString();
    }
}

форма

<!DOCTYPE html>
<html lang="en">
  <body>
    <script>
      function validateForm(form) {
        var username = document.getElementById("username").value;
        var password = document.getElementById("password").value;
        var error = document.getElementById("error");
        error.innerHTML = "";
        if (username === "" || password == "") {
          form.reset();
          error.innerHTML = "password / username empty";
          return false;
        }
        return true;
      }
    </script>

    <div class="container" style="width: 500px; float: left">
      <h2 class="text-center">Welcome to the App</h2>
      <form
        id="registerForm"
        method="POST"
        onsubmit="return validateForm(this)"
        action="Registration"
      >
        <div class="form-group">
          <label for="username">Username:</label>
          <input
            type="text"
            class="form-control"
            id="username"
            placeholder="Enter email"
            name="username"
          />
        </div>
        <div class="form-group">
          <label for="password">Password:</label>
          <input
            type="password"
            class="form-control"
            id="password"
            placeholder="Enter password"
            name="password"
          />
        </div>
        <div class="form-group form-check"></div>
        <button type="submit" class="btn btn-primary">Submit</button>
      </form>
      <div id="error" class="alert" role="alert"></div>
    </div>
  </body>
</html>

общие

//servelet context
ServletContext application=getServletContext();
application.setAttribute("messages", messages);



//SESSION
HttpSession session = request.getSession();
String name = (String) request.getParameter("username");
session.setAttribute("username", name);


//random id java
String uniqueID = UUID.randomUUID().toString();

//package
it.unitn.disi.webdev.claudiovigliarolo

//project name
VIGLIAROLO_C_202314

//get contextpath
    String contextPath = request.getContextPath();
        System.out.println("Context Path = " + contextPath);
        response.sendRedirect(contextPath + "/main.html");


//BOOTstrap
  <head>
    <title>Bootstrap Example</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
  </head>



//redirect to other page
window.location.replace("http://stackoverflow.com");



//CONTENT type
response.setContentType("application/json;charset=UTF-8");  
response.setContentType("text/html;charset=UTF-8");






//zuccherino
    protected final LinkedList<Message> list;
    
      public MessageList() {
        this.list = new LinkedList<>();
    }



<!DOCTYPE html>
<html lang="en">
<body>
<script>
function validateForm(form) {
    var username = document.getElementById("username").value;
     var password = document.getElementById("password").value;
      var error = document.getElementById("error");
      error.innerHTML = "";
    if(username === "" || password == "") {
         form.reset();
         error.innerHTML = "password / username empty";
         return false;
    }
   return true;
}
</script>

<div class="container " style="width:500px; float: left;">
   
  <h2 class="text-center">Welcome to the App</h2>
  <form id="registerForm" method="POST" onsubmit="return validateForm(this)" action="Registration">
    <div class="form-group">
      <label for="username">Username:</label>
      <input type="text" class="form-control" id="username" placeholder="Enter email" name="username">
    </div>
    <div class="form-group">
      <label for="password">Password:</label>
      <input type="password" class="form-control" id="password" placeholder="Enter password" name="password">
    </div>
    <div class="form-group form-check">
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
  </form>
 <div id="error" class="alert" role="alert">
  
</div>
</div>
</body>
</html>


//change styles
 const val =  keywords.some(k=>item.message.includes(k));
const color = val ? "#FFFF00;" : "transparent;";
document.getElementById("data").innerHTML +=
        "<div style=' margin-top:50px;flexdirection: row; display:flex; width:500px; background:" + color + "; justify-content: row; '>" +
        item.message +
        "</div>";


//setimeout
function refresh() {
// make Ajax call here, inside the callback call:
setTimeout(refresh, 5000);
// ...
}
// initial call, or just call refresh directly
setTimeout(refresh, 5000);


//template strings
`string text`

getclaudio postclaudio


<script>
  function onSendData() {
    var title = document.getElementById("username").value;
    var description = document.getElementById("password").value;
    console.log(title, description);
    var http = new XMLHttpRequest();
    var url = "Registration";
    var params = "password=" + description + "&username=" + title;
    http.open("POST", url, true);
    //Send the proper header information along with the request
    http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    http.onreadystatechange = function () {
      //Call a function when the state changes.
      if (http.readyState == 4 && http.status == 200) {
        console.log("res", http.responseText);
      }
    };
    http.send(params);
  }
</script>
------GET---------------
  //loadData noparam
<script>

  loadData();

function loadData() {
    var xhttp = new XMLHttpRequest();
    xhttp.open("GET", "GetProducts", true);
    xhttp.responseType = "json";
    xhttp.onreadystatechange = function () {
        var done = 4,
                ok = 200;
        if (this.readyState === done && this.status === ok) {
            my_JSON_object = this.response;
            console.log("response", my_JSON_object);
            document.getElementById("data").innerHTML = "";
            my_JSON_object && my_JSON_object.forEach((item) => {
                console.log("item", item.message);
                document.getElementById("data").innerHTML +=
                        " <div class='card' style='width: 300px; margin-top:50px;'>" +
                        "<div class='card-body'>" +
                        " <h4 class='card-title'>" + item.name + "</h4>" +
                        "<p class='card-text'>" + item.description + "</p>" +
                        "  <p class='card-text'>Price: " + item.price + " $</p>" +
                        "    <a href='detail.html?name=" + item.name + "' class='card-link' >View details</a>" +
                        "   </div>    </div>";
            });
        }
    };
    xhttp.send();
}
</script>
<div id="data"></div>

 //loadData withparam
<script>
  function getData() {
  const urlParams = new URLSearchParams(window.location.search);
  const id = urlParams.get('id');
  const id2 = urlParams.get('id2');

  console.log(id)
  var url = "GetItems";
  let param1 = id;
  let param2 = id2;
  var params = "param1=" + param1 + "&param2=" + param2;
  var http = new XMLHttpRequest();
  http.open("GET", url + "?" + params, true);
  http.responseType = "json";
  http.onreadystatechange = function () {
      var done = 4,
              ok = 200;
      if (this.readyState === done && this.status === ok) {
          my_JSON_object = this.response;
          console.log("response", my_JSON_object);
          document.getElementById("data").innerHTML = "";
          my_JSON_object && my_JSON_object.forEach((item) => {
              console.log("item", item.message);
              document.getElementById("data").innerHTML +=
                      "<div style=' margin-top:50px;flexdirection: row; display:flex; width:500px; justify-content: row; '>" +
                      item.message +
                      "</div>";
          });
      }
  };
  http.send(null);
}
</script>
<div id="data"></div>







<div class="card" style="margin-top: 50px;">
  <div class="card-body">Content</div>
</div>


//render multiple parameters
onclick="showData('${item.name}', '${item.price}', '${item.punteggio}', '${item.extra}' )"

JSON servelet claudio


RETURN JSON
//create simple json response
response.setContentType("application/json;charset=UTF-8");  
StringBuilder ret = new StringBuilder();
ret.append("{\"ready\":\"").append("false").append("\"}");
try (PrintWriter out = response.getWriter()) {
    out.println(ret.toString());
}

JSON LIST CLASS
     public String toJSON() {
        StringBuilder ret = new StringBuilder("[");
        String prefix = "";
        for(int i=0; i<list.size(); i++) {
            Message m = list.get(i);
            ret.append(prefix);
            prefix = ",";
            ret.append(m.toJson());
        }
        ret.append("]");
                System.err.println("priting tojson"+ ret.toString());
        return ret.toString();
    }


JSON ITEM
    public String toJson() {
        StringBuilder ret = new StringBuilder();
        ret.append("{\"id\":\"").append(this.id).append("\",");
        ret.append("\"nome\":\"").append(this.nome).append("\",");
        ret.append("\"imgName\":\"").append(this.imgName).append("\"}");
        return ret.toString();
    }




STRING LIST
    public String wordsToJSON(ArrayList<String> list) {
        System.err.println("lunghezza" + list.size());
        if (list.size() == 0) {
            return null;
        }
        StringBuilder ret = new StringBuilder("[");
        String prefix = "";
        for (int i = 0; i < list.size(); i++) {
            StringBuilder sb = new StringBuilder();
            ret.append(prefix);
            prefix = ",";
            ret.append(sb.append("{\"message\":\"").append(list.get(i)).append("\"}").toString());
        }

        ret.append("]");
        System.err.println("jjj" + ret.toString());
        return ret.toString();
    }

    }
JSON RESPONSE OK
           ServletContext application=getServletContext();
            MappaDiCoppie mappaDiCoppie =  (MappaDiCoppie) application.getAttribute("mappaDiCoppie");
            HttpSession session= request.getSession();
            String username = (String) session.getAttribute("username");
            StringBuilder ret = new StringBuilder();
            if(mappaDiCoppie != null && username != null)
            {
                if(mappaDiCoppie.exists(username))
                    //ok 
                    ret.append("{\"ready\":\"").append("true").append("\"}");
                else  
                    //no wait
                    ret.append("{\"ready\":\"").append("false").append("\"}")
                    try (PrintWriter out = response.getWriter()) {
                    out.println(ret.toString());
                }
            }


            ```
















person roman    schedule 15.07.2021
comment
Привет, @roman, не могли бы вы написать немного текста о своем решении или о том, как вы пытаетесь решить проблему с помощью своего кода? Кроме того, ваш ответ очень длинный, вы можете сосредоточиться на основной проблеме. - person Janos Vinceller; 16.07.2021

таблица стилей

 <link rel="stylesheet" href="./styles/styles.css">
  <script type="text/javascript" src="./js/script.js"></script>

матрица HTML

 const N = 9;
const container = document.getElementById("container");

function makeRows(rows, cols) {
    container.style.setProperty('--grid-rows', rows);
    container.style.setProperty('--grid-cols', cols);
    for (c = 0; c < (cols); c++) {
        for (r = 0; r < rows; r++) {
            const myid = JSON.stringify({x: r, y: c});
            let cell = document.createElement("div");
            var textnode = document.createElement("span");
            textnode.setAttribute("id", myid);
            cell.appendChild(textnode);
            cell.onclick = function (event) {
                showVal(myid, r, c);
            }
            //cell.innerText = (c + 1);
            container.appendChild(cell).className = "grid-item";
            container.appendChild(cell).style = "border-color: red";
            container.appendChild(cell).style = "border-width: 4px";

        }
        ;
    }
}

//styles

.myinput{
    width: 50px;
    margin-right: 20px;
}


#container {
  display: grid;
  grid-gap: .5em;
  grid-template-rows: repeat(var(--grid-rows), 1fr);
  grid-template-columns: repeat(var(--grid-cols), 1fr);
  width: 100px;
}

.grid-item {
  border: 1px solid #ddd;
  text-align: center;
  width: 50px;
  height: 50px;
  border-width: 2px;
  border-color: green;
  
}

сетка java

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package utils;

import java.util.ArrayList;
import java.util.Random;

/**
 *
 * @author claud
 */
public class Grid {

int N;
Cell[][] matrix;

public Grid(int N) {
    this.N = N;
    this.matrix = new Cell[N][N];
}

private int getRandom() {
    Random rn = new Random();
    int range = 0 - 0 + 1;
    int randomNum = rn.nextInt(N) + 0;
    return randomNum;
}

public int getValue(int x, int y) {
    if (x < N && y < N) {
        return this.matrix[x][y].value;
    } else {
        return -2;
    }
}

public void generate() {
    System.err.println("ffffffffffffggg");

    //fai il ciclo completo
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            this.matrix[i][j] = new Cell(i, j, 0);//aggiungi bombe con random altrimenti valore 0
        }
    }
}


public void print() {
    System.err.println("printiiiiiiiiiiiing START");
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            System.err.println(this.matrix[i][j].value);
        }
    }
}
}

person roman    schedule 15.07.2021