SSE System Documentation

Server-Sent Events Plugin System - HrFiskalizator

Verzija 1.0 | Real-time Communication & Monitoring

📋 Sadržaj Dokumentacije

🔍 Pregled Sustava

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.

🚀 Ključne Značajke

  • Real-time komunikacija
  • Plugin arhitektura
  • Automatska reconnection
  • Monitoring i statistike
  • Task-based messaging

🎯 Prednosti

  • Niska latencija
  • HTTP/HTTPS kompatibilnost
  • Firewall friendly
  • Jednostavna integracija
  • Cross-platform podrška
💡 Napomena: SSE sustav koristi jednosmjernu komunikaciju od servera prema klijentu, idealno za notifikacije, updates i real-time podatke.

🏗️ Arhitektura Sustava

Komponente Sustava

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/

Arhitekturni Dijagram

graph LR subgraph "Web Clients" A1[🌐 Browser Apps] A2[📱 Mobile Apps] A3[💻 Desktop Apps] A4[🔗 API Clients] end subgraph "SSEHandler (Java)" B1[🔌 Connection Management] B2[📋 Task Routing] B3[📊 Statistics] B4[🔄 Event Distribution] end subgraph "Plugin System" C1[⚡ Dynamic Load] C2[🎯 Task Execution] C3[🧩 Custom Logic] C4[📡 Event Handling] end A1 -.->|SSE Stream| B1 A2 -.->|SSE Stream| B1 A3 -.->|SSE Stream| B1 A4 -.->|REST API| B1 B1 --> B2 B2 --> B3 B3 --> B4 B2 -.->|Task Requests| C2 C2 -.->|Events| B4 C1 --> C2 C2 --> C3 C3 --> C4 classDef clientStyle fill:#e1f5fe,stroke:#0277bd,stroke-width:2px classDef serverStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px classDef pluginStyle fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px class A1,A2,A3,A4 clientStyle class B1,B2,B3,B4 serverStyle class C1,C2,C3,C4 pluginStyle

Data Flow

  1. Client Connection: Klijent se spaja na SSE endpoint
  2. Task Subscription: Klijent se pretplaćuje na specifične taskove
  3. Plugin Execution: Pluginovi se izvršavaju i generiraju eventi
  4. Event Distribution: Eventi se distribuiraju pretplaćenim klijentima
  5. Real-time Updates: Klijenti primaju ažuriranja u realnom vremenu

🌐 API Endpoints

SSE sustav pruža RESTful API za dohvaćanje informacija i statistika.

Glavni Endpoints

GET /sseapi/
Osnovne informacije o SSE serveru i dostupnim endpointovima.
GET /sseapi/clients
Lista aktivnih SSE klijenata s detaljima o konekcijama.
{
  "clients": [
    {
      "clientId": "web-client-1759588812045",
      "clientIP": "192.168.1.5",
      "connected": true,
      "tasks": ["status", "sms", "echo"]
    }
  ],
  "count": 1
}
            
GET /sseapi/tasks
Informacije o dostupnim taskovima (sistemski i plugin taskovi).
GET /sseapi/plugins
Lista registriranih pluginova s njihovim statusom.
GET /sseapi/stats
Detaljne sistemske statistike uključujući klijente, taskove i performance podatke.
{
  "systemInfo": {
    "totalTasks": 7,
    "activeClients": 2,
    "pluginTasks": 3
  },
  "allTasks": ["notification", "sms", "echo", "status"],
  "taskSubscribers": {
    "sms": 2,
    "echo": 2,
    "status": 2
  }
}
            

SSE Stream Endpoint

GET /sse
Glavni SSE stream endpoint za real-time komunikaciju.
Parameters: tasks (comma-separated lista taskova)
// 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);
};
            

📊 Monitoring Interface

Web-based monitoring interface omogućava real-time praćenje SSE sustava kroz intuitivno sučelje.

Glavne Funkcionalnosti

🔄 Server Information

Osnovne informacije o SSE serveru, verziji API-ja i dostupnim endpointovima.

👥 Active Clients

Real-time prikaz aktivnih klijenata s IP adresama i pretplaćenim taskovima.

📋 Available Tasks

Lista svih dostupnih taskova podijeljenih na sistemske i plugin taskove.

🔌 Registered Plugins

Pregled svih registriranih pluginova s njihovim statusom i verzijama.

📊 System Statistics

Detaljne statistike sustava uključujući performance podatke.

🌐 Connection Stats

Mrežne statistike, analiza konekcija po IP adresama i taskovima.

Kontrole Interface-a

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 System

Plugin arhitektura omogućava proširivanje funkcionalnosti SSE sustava kroz dinamički učitane JAR datoteke.

Plugin Struktura

// 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 Lifecycle

  1. Discovery: SSEHandler skenira SSEPLUGINS folder
  2. Loading: Dinamičko učitavanje JAR datoteka
  3. Registration: Registracija plugin taskova
  4. Execution: Izvršavanje plugin logike na zahtjev
  5. Monitoring: Praćenje plugin performansi

Dostupni Pluginovi

Plugin Task Name Opis
EchoPlugin echo Vraća poslane podatke (test plugin)
HelloWorldPlugin hello Demonstracija osnovnih funkcionalnosti
SmsPlugin sms SMS poruke s priority sustavom
⚠️ Napomena: Pluginovi se učitavaju dinamički pri pokretanju. Za dodavanje novih pluginova, postavite JAR datoteku u SSEPLUGINS folder i restartajte server.

👥 Client Management

SSE sustav upravlja klijentskim konekcijama i omogućava praćenje njihovog statusa u realnom vremenu.

Client Lifecycle

1. Connection

Klijent se spaja na SSE endpoint s listom željenih taskova.

2. Registration

Server registrira klijenta i dodjeljuje jedinstveni ID.

3. Subscription

Klijent se pretplaćuje na određene taskove za primanje eventi.

4. Communication

Real-time primanje eventi na osnovu pretplaćenih taskova.

Client Information

Za svakog klijenta sustav prati sljedeće informacije:

JavaScript Client Primjer

// 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 System

Task sistem omogućava organizaciju i distribuciju različitih tipova poruka kroz SSE komunikaciju.

Tipovi Taskova

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

Task Subscription

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=*');
        

Task Message Format

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"
}
        

Task Statistics

Sustav prati detaljne statistike za svaki task:

⚙️ Konfiguracija

SSE sustav može se konfigurirati kroz različite parametre i postavke.

Server Konfiguracija

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

Client Konfiguracija

// 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
];
        

Plugin Konfiguracija

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
        

🔧 Rješavanje Problema

Česti Problemi

Problem: Klijent se ne može spojiti na SSE

Mogući uzroci:

Rješenje:

  1. Provjeri status servera na /sseapi/
  2. Provjeri firewall postavke
  3. Provjeri browser konzolu za greške
  4. Provjeri CORS headere

Problem: Poruke se ne dostavljaju

Mogući uzroci:

Rješenje:

  1. Provjeri task subscription
  2. Provjeri plugin status
  3. Provjeri mrežnu konekciju
  4. Provjeri server statistike

Debugging Tools

🔍 Browser DevTools

Network tab za praćenje SSE konekcije

Console za JavaScript greške

📊 SSE Monitor

Real-time statistike

Client i plugin status

📝 Server Logs

Detaljno logiranje eventi

Error tracking

🧪 API Testing

Test endpoints direktno

Provjeri JSON odgovore

Performance Optimizacija

💡 Primjeri Korištenja

1. Jednostavan Chat Client

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

2. Real-time Status Dashboard

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

3. PHP Backend Integration

<?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'];
?>
        

4. JAVA Custom Plugin (Jednostavan plugin za push poruke)

/**
 * 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;
        }
    }
}