| |
1 |
| |
2 package de.unixwork.im; |
| |
3 |
| |
4 import java.io.IOException; |
| |
5 import java.util.ArrayList; |
| |
6 import java.util.List; |
| |
7 import java.util.concurrent.BlockingQueue; |
| |
8 import java.util.concurrent.LinkedBlockingQueue; |
| |
9 import java.util.logging.Level; |
| |
10 import java.util.logging.Logger; |
| |
11 import org.jivesoftware.smack.ConnectionConfiguration; |
| |
12 import org.jivesoftware.smack.SmackException; |
| |
13 import org.jivesoftware.smack.XMPPException; |
| |
14 import org.jivesoftware.smack.filter.MessageWithBodiesFilter; |
| |
15 import org.jivesoftware.smack.packet.Message; |
| |
16 import org.jivesoftware.smack.packet.MessageBuilder; |
| |
17 import org.jivesoftware.smack.roster.Roster; |
| |
18 import org.jivesoftware.smack.roster.RosterEntry; |
| |
19 import org.jivesoftware.smack.tcp.XMPPTCPConnection; |
| |
20 import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; |
| |
21 import org.jxmpp.stringprep.XmppStringprepException; |
| |
22 |
| |
23 public class Xmpp extends Thread { |
| |
24 private final XMPPTCPConnectionConfiguration config; |
| |
25 |
| |
26 private XMPPTCPConnection connection = null; |
| |
27 |
| |
28 // BlockingQueue for event-driven communication |
| |
29 private final BlockingQueue<XmppEvent> eventQueue = new LinkedBlockingQueue<>(); |
| |
30 |
| |
31 public Xmpp(XMPPTCPConnectionConfiguration xmppConfig) { |
| |
32 config = xmppConfig; |
| |
33 } |
| |
34 |
| |
35 // Method to send a message (this will be called from another thread) |
| |
36 public void sendMessage(String to, String message, boolean encrypted) { |
| |
37 try { |
| |
38 XmppMessage event = new XmppMessage(to, message, encrypted); |
| |
39 eventQueue.put(event); // Block if the queue is full |
| |
40 } catch (InterruptedException e) { |
| |
41 Logger.getLogger(Xmpp.class.getName()).log(Level.SEVERE, "Error adding event to queue", e); |
| |
42 } |
| |
43 } |
| |
44 |
| |
45 private void connect() throws SmackException, IOException, XMPPException, InterruptedException { |
| |
46 connection = new XMPPTCPConnection(config); |
| |
47 connection.setUseStreamManagement(false); |
| |
48 connection.connect(); |
| |
49 connection.login(); |
| |
50 |
| |
51 } |
| |
52 |
| |
53 public List<RosterEntry> getRosterItems() throws SmackException.NotLoggedInException, SmackException.NotConnectedException, InterruptedException { |
| |
54 try { |
| |
55 // Ensure we are connected |
| |
56 if (connection != null && connection.isConnected()) { |
| |
57 // Get the roster instance |
| |
58 Roster roster = Roster.getInstanceFor(connection); |
| |
59 |
| |
60 // Fetch all roster entries (contacts) |
| |
61 roster.reload(); |
| |
62 |
| |
63 // Add all roster entries to the list |
| |
64 ArrayList<RosterEntry> rosterList = new ArrayList<>(16); |
| |
65 roster.getEntries().forEach(entry -> rosterList.add(entry)); |
| |
66 |
| |
67 // Optionally, print the list to verify |
| |
68 System.out.println("Roster List: "); |
| |
69 for (RosterEntry entry : rosterList) { |
| |
70 System.out.println("Contact: " + entry.getUser()); |
| |
71 } |
| |
72 |
| |
73 return rosterList; |
| |
74 } else { |
| |
75 System.out.println("Not connected to XMPP server."); |
| |
76 } |
| |
77 } catch (SmackException e) { |
| |
78 Logger.getLogger(Xmpp.class.getName()).log(Level.SEVERE, "Error getting roster items", e); |
| |
79 } |
| |
80 |
| |
81 return null; |
| |
82 } |
| |
83 |
| |
84 @Override |
| |
85 public void run() { |
| |
86 try { |
| |
87 connect(); |
| |
88 connection.addAsyncStanzaListener((stanza -> { |
| |
89 var jid = stanza.getFrom(); |
| |
90 if(jid != null) { |
| |
91 String body = ((Message)stanza).getBody(); |
| |
92 App.getInstance().dispatchMessage(jid, body, true); |
| |
93 } |
| |
94 }), MessageWithBodiesFilter.INSTANCE); |
| |
95 List<RosterEntry> roster = getRosterItems(); |
| |
96 if(roster != null) { |
| |
97 App.getInstance().setContacts(roster); |
| |
98 } |
| |
99 |
| |
100 while (true) { |
| |
101 // Wait for an event (message to send) |
| |
102 XmppEvent event = eventQueue.take(); // This will block until an event is available |
| |
103 event.exec(this, connection); |
| |
104 } |
| |
105 } catch (SmackException ex) { |
| |
106 Logger.getLogger(Xmpp.class.getName()).log(Level.SEVERE, null, ex); |
| |
107 } catch (IOException ex) { |
| |
108 Logger.getLogger(Xmpp.class.getName()).log(Level.SEVERE, null, ex); |
| |
109 } catch (XMPPException ex) { |
| |
110 Logger.getLogger(Xmpp.class.getName()).log(Level.SEVERE, null, ex); |
| |
111 } catch (InterruptedException ex) { |
| |
112 Logger.getLogger(Xmpp.class.getName()).log(Level.SEVERE, null, ex); |
| |
113 } |
| |
114 } |
| |
115 } |
| |
116 |
| |
117 class XmppMessage implements XmppEvent { |
| |
118 String to; |
| |
119 String message; |
| |
120 boolean encrypted; |
| |
121 |
| |
122 XmppMessage(String to, String message, boolean encrypted) { |
| |
123 this.to = to; |
| |
124 this.message = message; |
| |
125 this.encrypted = encrypted; |
| |
126 } |
| |
127 |
| |
128 public void exec(Xmpp xmpp, XMPPTCPConnection conn) { |
| |
129 final Message msg; |
| |
130 try { |
| |
131 msg = MessageBuilder.buildMessage() |
| |
132 .to(to) |
| |
133 .setBody(message) |
| |
134 .build(); |
| |
135 conn.sendStanza(msg); |
| |
136 } catch (XmppStringprepException ex) { |
| |
137 Logger.getLogger(XmppMessage.class.getName()).log(Level.SEVERE, null, ex); |
| |
138 } catch (SmackException.NotConnectedException ex) { |
| |
139 Logger.getLogger(XmppMessage.class.getName()).log(Level.SEVERE, null, ex); |
| |
140 } catch (InterruptedException ex) { |
| |
141 Logger.getLogger(XmppMessage.class.getName()).log(Level.SEVERE, null, ex); |
| |
142 } |
| |
143 } |
| |
144 } |