Server-Sent Events Plugin System - HrFiskalizator
Verzija 1.0 | Real-time Communication & Monitoring
SSE (Server-Sent Events) sustav u HrFiskalizatoru omogućava real-time komunikaciju između servera i klijenata putem standardnog HTTP protokola. Sustav je dizajniran za visoku performansu, skalabilnost i jednostavnost korištenja.
| Komponenta | Funkcija | Lokacija |
|---|---|---|
| SSEHandler.java | Glavni server handler | src/fiskalizacija/ |
| sse.js | JavaScript monitoring client | web/ |
| sse.php | Web monitoring interface | web/ |
| Plugin JARs | Proširivi plugin sustav | SSEPLUGINS/ |
SSE sustav pruža RESTful API za dohvaćanje informacija i statistika.
{
"clients": [
{
"clientId": "web-client-1759588812045",
"clientIP": "192.168.1.5",
"connected": true,
"tasks": ["status", "sms", "echo"]
}
],
"count": 1
}
{
"systemInfo": {
"totalTasks": 7,
"activeClients": 2,
"pluginTasks": 3
},
"allTasks": ["notification", "sms", "echo", "status"],
"taskSubscribers": {
"sms": 2,
"echo": 2,
"status": 2
}
}
// Primjer konekcije
const eventSource = new EventSource('/sse?tasks=sms,status,echo');
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
Web-based monitoring interface omogućava real-time praćenje SSE sustava kroz intuitivno sučelje.
Osnovne informacije o SSE serveru, verziji API-ja i dostupnim endpointovima.
Real-time prikaz aktivnih klijenata s IP adresama i pretplaćenim taskovima.
Lista svih dostupnih taskova podijeljenih na sistemske i plugin taskove.
Pregled svih registriranih pluginova s njihovim statusom i verzijama.
Detaljne statistike sustava uključujući performance podatke.
Mrežne statistike, analiza konekcija po IP adresama i taskovima.
| Ikona | Funkcija | Opis |
|---|---|---|
| 🔄 | Refresh Stats | Ručno osvježavanje svih statistika |
| 🗑️ | Clear Editor | Brisanje sadržaja editora |
| 📄 | Toggle Format | Prebacivanje između čitljivog i JSON formata |
| 📋 | Copy to Clipboard | Kopiranje statistika u clipboard |
| 💾 | Save Stats | Spremanje statistika u datoteku |
| ⏰ | Auto-refresh | Automatsko osvježavanje (10s interval) |
Plugin arhitektura omogućava proširivanje funkcionalnosti SSE sustava kroz dinamički učitane JAR datoteke.
// Osnovna plugin klasa
public class MyPlugin {
public String getTaskName() {
return "my-custom-task";
}
public String getDescription() {
return "Opis mog plugina";
}
public String getVersion() {
return "1.0.0";
}
public boolean isEnabled() {
return true;
}
public String executeTask(String parameters) {
// Plugin logika
return "Rezultat izvršavanja";
}
}
| Plugin | Task Name | Opis |
|---|---|---|
| EchoPlugin | echo | Vraća poslane podatke (test plugin) |
| HelloWorldPlugin | hello | Demonstracija osnovnih funkcionalnosti |
| SmsPlugin | sms | SMS poruke s priority sustavom |
SSE sustav upravlja klijentskim konekcijama i omogućava praćenje njihovog statusa u realnom vremenu.
Klijent se spaja na SSE endpoint s listom željenih taskova.
Server registrira klijenta i dodjeljuje jedinstveni ID.
Klijent se pretplaćuje na određene taskove za primanje eventi.
Real-time primanje eventi na osnovu pretplaćenih taskova.
Za svakog klijenta sustav prati sljedeće informacije:
// Spajanje na SSE stream
const eventSource = new EventSource('/sse?tasks=sms,status,echo');
// Rukovanje događajima
eventSource.onopen = function(event) {
console.log('SSE connection opened');
};
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Received message:', data);
// Obrada različitih tipova poruka
switch(data.task) {
case 'sms':
handleSmsMessage(data);
break;
case 'status':
updateStatus(data);
break;
case 'echo':
displayEcho(data);
break;
}
};
eventSource.onerror = function(event) {
console.error('SSE connection error:', event);
// Automatska reconnection logika
setTimeout(() => {
eventSource.close();
connectToSSE(); // Ponovni pokušaj spajanja
}, 5000);
};
Task sistem omogućava organizaciju i distribuciju različitih tipova poruka kroz SSE komunikaciju.
| Tip | Opis | Primjeri |
|---|---|---|
| System Tasks | Ugrađeni sistemski taskovi | notification, status, task-update |
| Plugin Tasks | Taskovi definirani u pluginovima | sms, echo, hello |
| Custom Tasks | Prilagođeni taskovi aplikacije | user-defined tasks |
Klijenti se mogu pretplatiti na jedan ili više taskova:
// Pretplata na pojedinačni task
const sseClient = new EventSource('/sse?tasks=sms');
// Pretplata na više taskova
const sseClient = new EventSource('/sse?tasks=sms,status,echo');
// Pretplata na sve dostupne taskove
const sseClient = new EventSource('/sse?tasks=*');
Sve SSE poruke slijede standardni JSON format:
{
"task": "sms",
"timestamp": "2025-10-04T15:30:00Z",
"data": {
"message": "Test SMS poruka",
"priority": "high",
"sender": "system"
},
"clientId": "web-client-1759588812045",
"eventId": "evt-12345"
}
Sustav prati detaljne statistike za svaki task:
SSE sustav može se konfigurirati kroz različite parametre i postavke.
| Parametar | Zadana Vrijednost | Opis |
|---|---|---|
| SSE_PORT | Port HrFiskalizatora | Port za SSE server |
| MAX_CLIENTS | 1000 | Maksimalni broj klijената |
| HEARTBEAT_INTERVAL | 30s | Interval heartbeat poruka |
| PLUGIN_DIR | SSEPLUGINS/ | Folder za plugin datoteke |
| LOG_LEVEL | INFO | Razina logiranja |
// JavaScript client konfiguracija
const sseConfig = {
url: '/sse',
tasks: ['sms', 'status'],
reconnectInterval: 5000,
maxReconnectAttempts: 10,
heartbeatTimeout: 45000
};
// PHP konfiguracija
$sseConfig = [
'base_url' => 'http://localhost:8449',
'timeout' => 30,
'retry_count' => 3
];
Pluginovi mogu imati vlastite konfiguracijske datoteke:
// plugin.properties
plugin.name=SmsPlugin
plugin.version=1.0.0
plugin.enabled=true
plugin.priority=normal
plugin.timeout=10000
# SMS specifične postavke
sms.provider=twillio
sms.api_key=your_api_key
sms.from_number=+1234567890
Mogući uzroci:
Rješenje:
Mogući uzroci:
Rješenje:
Network tab za praćenje SSE konekcije
Console za JavaScript greške
Real-time statistike
Client i plugin status
Detaljno logiranje eventi
Error tracking
Test endpoints direktno
Provjeri JSON odgovore
<!DOCTYPE html>
<html>
<head>
<title>SSE Chat Example</title>
</head>
<body>
<div id="messages"></div>
<input type="text" id="messageInput" placeholder="Upišite poruku...">
<button onclick="sendMessage()">Pošalji</button>
<script>
// Spoji se na SSE
const eventSource = new EventSource('/sse?tasks=chat');
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.task === 'chat') {
displayMessage(data.data.message, data.data.user);
}
};
function displayMessage(message, user) {
const messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML += `<p><strong>${user}:</strong> ${message}</p>`;
}
function sendMessage() {
const input = document.getElementById('messageInput');
const message = input.value.trim();
if (message) {
// Pošalji poruku preko API-ja
fetch('/api/chat/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: message,
user: 'Anonymous'
})
});
input.value = '';
}
}
</script>
</body>
</html>
// React komponenta za status dashboard
import React, { useState, useEffect } from 'react';
function StatusDashboard() {
const [status, setStatus] = useState({});
const [connected, setConnected] = useState(false);
useEffect(() => {
const eventSource = new EventSource('/sse?tasks=status,monitoring');
eventSource.onopen = () => setConnected(true);
eventSource.onclose = () => setConnected(false);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
switch(data.task) {
case 'status':
updateSystemStatus(data.data);
break;
case 'monitoring':
updateMonitoringData(data.data);
break;
}
};
return () => eventSource.close();
}, []);
const updateSystemStatus = (data) => {
setStatus(prev => ({
...prev,
system: data
}));
};
const updateMonitoringData = (data) => {
setStatus(prev => ({
...prev,
monitoring: data
}));
};
return (
<div className="dashboard">
<h2>System Status {connected ? '🟢' : '🔴'}</h2>
<div className="status-grid">
<div className="status-card">
<h3>CPU Usage</h3>
<p>{status.system?.cpu || 0}%</p>
</div>
<div className="status-card">
<h3>Memory Usage</h3>
<p>{status.system?.memory || 0}%</p>
</div>
<div className="status-card">
<h3>Active Users</h3>
<p>{status.monitoring?.activeUsers || 0}</p>
</div>
</div>
</div>
);
}
export default StatusDashboard;
<?php
/**
* PHP klasa za komunikaciju s SSE sustavom
*/
class SSEClient {
private $baseUrl;
private $timeout;
public function __construct($baseUrl = 'http://localhost:8449', $timeout = 30) {
$this->baseUrl = $baseUrl;
$this->timeout = $timeout;
}
/**
* Pošalji poruku preko SSE sustava
*/
public function sendMessage($task, $data, $targetClients = null) {
$payload = [
'task' => $task,
'data' => $data,
'timestamp' => date('c'),
'targets' => $targetClients
];
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode($payload),
'timeout' => $this->timeout
]
]);
$response = file_get_contents(
$this->baseUrl . '/api/sse/send',
false,
$context
);
return json_decode($response, true);
}
/**
* Dohvati statistike SSE sustava
*/
public function getStats() {
$response = file_get_contents(
$this->baseUrl . '/sseapi/stats',
false,
stream_context_create([
'http' => ['timeout' => $this->timeout]
])
);
return json_decode($response, true);
}
/**
* Dohvati aktivne klijente
*/
public function getActiveClients() {
$response = file_get_contents(
$this->baseUrl . '/sseapi/clients',
false,
stream_context_create([
'http' => ['timeout' => $this->timeout]
])
);
return json_decode($response, true);
}
}
// Primjer korištenja
$sse = new SSEClient();
// Pošalji SMS notifikaciju
$sse->sendMessage('sms', [
'message' => 'Nova fiskalna potvrda kreirana',
'priority' => 'high',
'recipient' => '+385981234567'
]);
// Pošalji status update
$sse->sendMessage('status', [
'status' => 'processing',
'progress' => 75,
'operation' => 'invoice_generation'
]);
// Dohvati trenutne statistike
$stats = $sse->getStats();
echo "Aktivnih klijenta: " . $stats['systemInfo']['activeClients'];
?>
/**
* Primjer custom plugin-a za email notifikacije
*/
public class EmailNotificationPlugin {
public String getTaskName() {
return "email";
}
public String getDescription() {
return "Email notification plugin za slanje obavještenja";
}
public String getVersion() {
return "1.0.0";
}
public boolean isEnabled() {
return true;
}
public String executeTask(String parameters) {
try {
// Parse JSON parametara
JSONObject params = new JSONObject(parameters);
String to = params.getString("to");
String subject = params.getString("subject");
String message = params.getString("message");
String priority = params.optString("priority", "normal");
// Pošalji email
boolean success = sendEmail(to, subject, message, priority);
// Vrati rezultat
JSONObject result = new JSONObject();
result.put("success", success);
result.put("timestamp", new Date().toInstant().toString());
result.put("recipient", to);
return result.toString();
} catch (Exception e) {
// Handle greške
JSONObject error = new JSONObject();
error.put("success", false);
error.put("error", e.getMessage());
return error.toString();
}
}
private boolean sendEmail(String to, String subject, String message, String priority) {
// Implementiraj email slanje logiku
// Koristi JavaMail API ili drugi email provider
try {
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("your_email", "your_password");
}
});
Message emailMessage = new MimeMessage(session);
emailMessage.setFrom(new InternetAddress("noreply@hrfiskalizator.com"));
emailMessage.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
emailMessage.setSubject(subject);
emailMessage.setText(message);
// Postavi prioritet
if ("high".equals(priority)) {
emailMessage.setHeader("X-Priority", "1");
} else if ("low".equals(priority)) {
emailMessage.setHeader("X-Priority", "5");
}
Transport.send(emailMessage);
return true;
} catch (MessagingException e) {
System.err.println("Email sending failed: " + e.getMessage());
return false;
}
}
}