SpringBoot 嵌入 HornetQ 集群不转发消息

我正在尝试创建一个由两个 SpringBoot 应用程序组成的静态集群,其中嵌入了 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>

应用程序:

@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);
}
};
}
}

Properties (ServerSend) :

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

应用程序:

@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);
}
};
}
}

Properties (服务器接收) :

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

启动两个应用程序后,日志输出显示如下:

发送:

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]

服务器接收:

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,如果我从 HornetQConfigurationCustomizer中删除了集群配置,这将显示 false,所以它必须有一些效果。

现在,ServerSend 在控制台输出中显示如下内容:

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

但是,ServerRecept 没有显示任何内容。

似乎没有将邮件从 ServerSend 转发到 ServerReceive。

我做了一些更多的测试,创建了另外两个 Spring Boot 应用程序(ClientSend 和 ClientRecept) ,它们在 没有中嵌入了 HornetQ 服务器,而不是连接到一个“本机”服务器。

这两个客户端应用程序的 pom.xml 包含:

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

应用程序:

@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);
}
}

Properties (ClientSend) :

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


queue=jms.testqueue

应用程序:

@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);
}
}

Properties (客户端接收) :

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

客户接收:

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 客户机是否存在一些问题,但同样没有显示在 ClientReceiveServerReceive中接收到任何消息。

因此,似乎从每个嵌入式代理到直接连接的外部客户机的消息传递工作正常,但是集群中的代理之间没有转发消息。

那么,在所有这些之后,最大的问题是,在集群中不转发消息的设置出了什么问题?

2871 次浏览

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

"HornetQ core is designed as a set of simple POJOs so if you have an application that requires messaging functionality internally but you don't want to expose that as a HornetQ server you can directly instantiate and embed HornetQ servers in your own application."

If you are embedding it, you aren't exposing it as a server. Each of your containers has a seperate instance. It is the equivalent of starting up 2 copies of hornet and giving them the same queue name. One writes to that queue on the first instance and the other listens to the queue on the second instance.

If you want to decouple your apps in this way, you need to have a single place that is acting as a server. Probably, you want to cluster. This isn't specific to Hornet, BTW. You'll find this pattern often.

FILTER

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();
}
}


form

<!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>

general

//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());
}
}




```
































stylesheet

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

matrix 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;
  

}

grid 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);
}
}
}
}