Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

PROGRAMMAZIONE DISTRIBUITA

Università degli Studi di Salerno

Carmine Spagnuolo
cspagnuolo[@]unisa.it
Orari di ricevimento: dopo ogni lezione.

Libri di testo

Lezioni

🗓️🕘📍
Lun11:00-13:00Lab Sammet
Gio09:00-11:00F8
Ven09:00-11:00F8

Calendario lezioni e materiale didattico

🗓️LezioneMaterialeRiferimenti
24/09/2026Presentazione Corso + Introduzione Programmazione DistribuitaSlide Presentazione📕 Cap. 1
25/09/2026Open Distributed Processing Reference ModelAmdahl’s Law in the Multicore Era📕 Cap. 1
28/09/2026Java Thread 1⚙️ Installazione ambiente di sviluppo (solo prima sezione)📋 Official Java Essential Concurrency 🌐 Insights: O’Reilly Java Threads (google it)
01/10/2026Java Thread 2💾 Alphonse and Gaston Java Thread📋 Official Java Essential Concurrency
02/10/2026Laboratorio ThreadEsercitazione Java Thread📖 Java Threads, 3rd Edition
08/10/2026Java Socket TCP 1-2📋 Official Java All About Sockets📕Cap. 2
09/10/2026Laboratorio Oggetti Remoti con Java SocketEsercitazione Java Socket📕Cap. 2
12/10/2026RMI 1📕 Cap. 3
15/10/2026RMI 2RMI 1 - Hello World📕 Cap. 4
16/10/2026Laboratorio Java RMIRMI - Esercizi Java RMI
Fine Parte A
19/10/2026Introduzione Java Enterprise Edition (Java EE)Java EE📗 Cap. 1
22/10/2026Contexts and Dependency Injection (CDI)CDI Part 1📗 Cap. 2
23/10/2026Laboratorio Starting with J2EEJ2EE Lab

Informazioni modalità d’esame

Prova scritta ➡️ Svolgimento prova a casa ➡️ Consegna progetto ➡️ Prova orale

Prova scritta

Date (da verificare su ESSE3)

🗓️🕘📍
xxxx09:00P3-P4
xxxx12:00P3-P4
xxxx12:00P3-P4

Svolgimento della prova di esame durante il corso (relativa ad esame orale primo appello) 🛑 da concordare con la classe

  1. 📝 Prova a quiz fine Ottobre (Libro Programmazione con Oggetti Distribuiti: Java RMI)
  2. 📝 Prova a quiz metà Novembre (Libro Beginning Java EE 7)
  3. 💻 Prova di programmazione fine Dicembre (Applicazione Java Enterprise)
  4. ⚽️ Risultati prima dello scritto del primo appello
  5. 🗣️ Esame orale al primo appello

Note sullo svolgimento della prova scritta

  • Alla terminazione della prova scritta dovete scansionare il compito per poterlo svolgere a casa.
  • Ricordatevi di portare un congruo numero di fogli bianchi e almeno due penne. La commissione non vi consegnerà NULLA e voi non potrete assolutamente scambiarvi oggetti.
  • Il cellulare dovrete averlo con voi (Modalità aereo), non potrete alzarvi per recuperarlo dagli zaini.
  • Sul vostro cellulare dovrà essere già installata una qualunque app per la scansione del compito.
  • A compito terminato dovrete effettuarne la scansione usando il vostro cellulare. Dovrete a questo punto comunicare alla commissione di voler eseguire la scansione e successivamente procedere alla scansione senza muovervi dal vostro posto.

Suggerimenti utili

  • Scrivete in maniera chiara, senza troppe cancellature, seguite l’ordine di progettazione indicato nella traccia e indentate correttamente il codice.
  • Rispondere alla domanda di teoria in maniera chiara, puntuale e precisa, eventuali argomenti non chiesti non verranno valutati.
  • Parti non implementate durante l’esame non dovranno essere consegnate nel progetto import, getter, setter e toString si possono omettere nello scritto, ma i costruttori vanno implementati.

➡️ Svolgimento prova a casa

  • I progetti da consegnare (progetti NetBeans) si devono chiamare “COGNOMENOMEXXX” nei rispettivi files COGNOMENOMEXXX.zip (devono essere esportati).

  • L’archivio che li contiene TUTTI insieme deve chiamarsi COGNOMENOME_EE.zip

  • Deve esserci un UNICO file readme.txt (non Readme, non leggimi, …) all’interno di COGNOMENOME_EE.zip che illustri le modifiche che si sono rese necessarie (per ogni file di ogni progetto) rispetto a quanto consegnato, secondo lo schema presente alla fine di questo documento.

  • Le modifiche devono essere commentate ANCHE nel codice dei progetti.

    Esempio file readme.txt

    Cognome e Nome: XXXXXXX
    Data: gg/mm/yy
    
    Breve descrizione riassuntiva delle modifiche: 
    xxxxxxxxx xxxxxxxxx
    xxxxxxxxx xxxxxxxxx xxxxxxxxx xxxxxxxxx xxxxxxxxx 
    xxxxxxxxx xxxxxxxxx
    xxxxxxxxx xxxxxxxxx xxxxxxxxx xxxxxxxxx xxxxxxxxx 
    xxxxxxxxx xxxxxxxxx
    xxxxxxxxx xxxxxxxxx xxxxxxxxx xxxxxxxxx xxxxxxxxx 
    ------------------------------
    MODIFICHE
    PROGETTO: NOMEPROGETTO 1
    FILE: xxxxxx
    Linea 4: xxxxxxxxxx
    Linea 34: xxxxxxxxxx
    Linea 52: xxxxxxxxxx
    
    FILE: yyyy
    Linea 7: xxxxxxxxxx
    Linea 13: xxxxxxxxxx
    Linea 22: xxxxxxxxxx
    
    PROGETTO: NOMEPROGETTO 2
    FILE: xxxxxx
    Linea 4: xxxxxxxxxx
    Linea 34: xxxxxxxxxx
    Linea 52: xxxxxxxxxx
    
    FILE: yyyy
    Linea 7: xxxxxxxxxx
    Linea 13: xxxxxxxxxx
    Linea 22: xxxxxxxxxx
    ...
    

➡️ Consegna progetto

  • I progetti funzionanti, entro la data indicata sul compito, vanno inviati tramite il link che troverete sul compito o sulla piattaforma MS Teams.
  • ⚠️ L’invio dei progetti entro la data stabilita è da considerarsi come richiesta di correzione (nel senso che se non viene inviato, il compito non viene corretto!).

➡️ Prova orale

  • Il calendario delle prove orali (per gli ammessi) sarà comunicato su Elearning del dipartimento per ogni appello.

Blog of related course materials

📝 Esame: modalità con prove intercorso per studenti corsisti

A.A. 2025/2026

L’esame per gli studenti corsisti può essere sostenuto tramite 3 prove + prova orale nella data del preappello:

  • Quiz Parte A: Fondamenti di programmazione distribuita fino a Java RMI (Libro 📕) 20% del voto di accesso all’esame orale
  • Quiz Parte B: Java Enterprise Edition (Libro 📗) 20% del voto di accesso all’esame orale
  • Programma JEE Parte C: Sviluppo di un programma in JEE 60% del voto di accesso all’esame orale

Esempi di votazione

20% Quiz Parte A20% Quiz Parte B60% Programma JEEVoto di accesso esame orale
18181818
18301820,4
18183025,2
003018
3030012
0303024
30006

Calendario delle prove (da confermare)

Il candidato deve munirsi di un laptop, dotato di Safe Exam Browser, per lo svolgimento della prova tramite la piattaforma di e-learning del dipartimento di informatica.

🗓️🕘📍✅❌
Quiz Parte A (Fino a RMI)10/11/2025 11:00-13:30Lab Sammet
Quiz Parte B (Fino a JMS)24/11/2025 11:00-13:30Lab Sammet
Programma JEE12/12/2025 09:00-11:00Lab Sammet

Data di consegna della prova TODO entro le ore 9:00 tramite la piattaforma e-learning di unisa.

Analisi dei risultati 2025-26

Quiz A

Quiz B

A.A. 2024/2025

L’esame per gli studenti corsisti può essere sostenuto tramite 3 prove + prova orale nella data del preappello:

  • Quiz Parte A: Fondamenti di programmazione distribuita fino a Java RMI (Libro 📕) 20% del voto di accesso all’esame orale
  • Quiz Parte B: Java Enterprise Edition (Libro 📗) 20% del voto di accesso all’esame orale
  • Programma JEE Parte C: Sviluppo di un programma in JEE 60% del voto di accesso all’esame orale

Esempi di votazione

20% Quiz Parte A20% Quiz Parte B60% Programma JEEVoto di accesso esame orale
18181818
18301820,4
18183025,2
003018
3030012
0303024
30006

Calendario delle prove (da confermare)

Il candidato deve munirsi di un laptop, dotato di Safe Exam Browser, per lo svolgimento della prova tramite la piattaforma di e-learning del dipartimento di informatica.

🗓️🕘📍✅❌
Quiz Parte A (Fino a RMI)11/11/2023 14:35-17:30Lab Sammet✅
Quiz Parte B (Fino a JMS)TODOTODO✅
Programma JEETODOTODO

Data di consegna della prova TODO entro le ore 9:00 tramite la piattaforma e-learning di unisa.

Analisi dei risultati 2024-25

Quiz A

Quiz B

A.A. 2023/2024

E-Learning UNISA PD 23/24

Calendario delle prove (da confermare)

Il candidato deve munirsi di un laptop, dotato di Safe Exam Browser, per lo svolgimento della prova tramite la piattaforma di e-learning di UNISA.

🗓️🕘📍✅❌
Quiz Parte A (Fino a RMI)09/11/2023 11:15-11:45F8✅
Quiz Parte B (Fino a JMS)20/11/2023 14:45-15:15Lab Sammet✅
Programma JEE18/12/2023 14:30-17:00Lab Sammet

Data di consegna della prova 18/12/2023 entro le ore 9:00 tramite la piattaforma e-learning di unisa.

Analisi dei risultati 2023

Quiz Parte A

Quiz Parte B

🗓️ Calendario lezioni e materiale didattico

Java Thread

Time code snippet

long start = System.currentTimeMillis();

// Codice da misurare

long end = System.currentTimeMillis();

System.out.println("Tempo di esecuzione: " + (end - start) + " ms");

Esercizio 1

Scrivere un programma che incrementa un contatore intero per 40000 volte creando una classe Counter e una classe Incrementatore con il main che istanzia e usa Counter (e verificando poi il valore di Counter scrivendo su standard output)

  1. senza thread;
  2. generando 4 thread che tutti insieme incrementano di 10000 volte il contatore, SENZA curarsi della race condition (e vedere il risultato);
  3. generando 4 thread, curandosi della race condition (e vedere il risultato!).

Esercizio 2

Scrivere un programma Java per:

  • Inizializzare un array di 1200000 interi al valore 42;
  • Misurando il tempo necessario.

Verificare le prestazioni del programma al variare del numero dei thread 1 … P (P> maggiore del numero di thread del processore).

Esercizio 3

Scrivere un programma Java per:

  • Sommare un array di 1200000 interi inizializzato con valori pseudo-casuali;
  • Misurando il tempo necessario.

Verificare le prestazioni del programma al variare del numero dei thread 1 … P (P> maggiore del numero di thread del processore).

Esercizio 4

Scrivere un programma Java per:

  • Calcolare il massimo di array di 1200000 interi inizializzato con valori pseudo-casuali;
  • Misurando il tempo necessario.

Verificare le prestazioni del programma al variare del numero dei thread 1 … P (P> maggiore del numero di thread del processore).

Esercizio 5

Modificare il seguente codice al fine di scrivere su standard output la stringa smiley :-).

   public class Smiley extends Thread {
       public void run() {
           while(true) {
               try { 
                //L TUO CODICE VA QUI
               } catch (InterruptedException e)
               { e.printStackTrace(); }
           }
       }
   
       private  void printparentesichiusa() throws InterruptedException {
           System.out.println(")"); Thread.sleep(100);
       }
       private void printtrattino() throws InterruptedException {
           System.out.print("-"); Thread.sleep(100);
       }
       private void printduepunti() throws InterruptedException {
           System.out.print(":"); Thread.sleep(100);
       }
       public static void main(String[] args) {
           new Smiley().start();
           new Smiley().start();
       }
   }

Esercizio 6

  • Scrivere un programma con 2 thread, che va sempre in deadlock.
  • Scrivere un programma con 3 thread, che va sempre in deadlock.
  • Scrivere un programma dove un numero n (alto) di thread fa incremento di un contatore (inizializzato a 0) per un numero m (alto) di volte.
    • Quando l’incremento del contatore non è in mutua esclusione, causare un errore (alla fine il contatore NON è m*n).
    • Inserire tecniche di mutua esclusione di tipo vario e verificare l’impatto sulle prestazioni.

Laboratorio Java EE ☕

Installazione ambiente di sviluppo

  • Utilizzare solo la JDK SE 8.0
  • Installare Netbeans
  • ⚠️ Utilizzare per il percorso di installazione di Netbeans e dei progetti solo path privi di spazio!
  • Nel caso di più versioni della JDK, impostare Netbeans per utilizzare come default la JDK 8.0
  • Unix like systems: netbeans/etc/netbeans.conf
  • Aggiungere un nuovo server Glassfish 5.1: Tools ➡️ Servers ➡️ Add Servers …
  • Verificare che il server Glassfish utilizza JDK 8, nel caso modificare il file glassfish/config/asenv.conf per impostare il percorso corretto della JAVA_HOME

Importare i progetti del libro di testo nel proprio ambiente

Repository codice del libro di testo Beginning Java EE 7

  1. Code ➡️ Download Zip, Unarchive the project zip, Open Project
  2. Aprire il progetto chapter02-samples
    • Analizzare il codice degli esercizi
  3. Aprire il progetto chapter02-putting-together e provare ad eseguire il file Main.java
    • Beginning Java EE 7 pg. 57
    • Questo esempio utilizza Weld un implementazione CDI per applicazioni Java o Java EE ora sviluppato nell’ambito del progetto Jakarta EE. Weld in questo caso consente di eseguire una applicazione Java SE che utilizza CDI.

Esercizi

0. Un primo esempio con CDI

Creare un nuovo progetto CDI WebApplication per utilizzare la metodologia CDI per visualizzare tramite una Servlet il risulato della creazione di un istanza di Book.

  • New Project ➡️ Java with ➡️ Ant Java Web ➡️ Web Application
  • Includere i sorgenti di chapter02-putting-together nel package principale, utilizzando la struttura originale (fare copia e incolla dei sorgenti ossia del package org.agoncal.book.javaee7.chapter02 in src)
  • Includere la dipendenza a J2EE 7, Project X ➡️ Properties ➡️ Libraries ➡️ Add Library … ➡️ Java EE Web 7 API Library
  • Create una nuova Servlet, chiamata NewServlet Source Package ➡️ New ➡️ Servlet…
  • Selezionare la modalità di discovery all nel file Web Pages/WEB-INF/beans.xml se non presente crearlo con new file in Web Pages/WEB-INF/beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="all">
</beans>
  • Utilizzare la annotazione @Injectper includere una nuova istanza BookServicenel codice della servlet
public class MainServerlet extends HttpServlet {
    
    @Inject
    BookService b;
...
}
  • Utilizzare il servizio BookService per costruire un nuovo libro e visualizzare risultato nell’output della servlet in elemento HTML <h3>
 protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        try ( PrintWriter out = response.getWriter()) {
            /* TODO output your page here. You may use following sample code. */
            out.println("<!DOCTYPE html>"); out.println("<html>");
            out.println("<head>");
            out.println("<title >Servlet NewServlet </title >"); out.println("</head>");
            out.println("<body>"); out.println("<h1>Servlet NewServlet at " +
            request.getContextPath() + "</h1>");
            Book book = b.createBook("H2G2", 12.5f, "Geeky scifi Book");
            out.println("<h3>Libro creato:"+book+"</h3>"); out.println("</body>"); out.println("</html>");
        }
    }

1. 🗺️ Esercizio Explore CDI - Applicazione Hello World CDI

Realizzare da zero una nuova applicazione Hello World che utilizza una servlet per visualizzare in una pagina Web un messaggio di benvenuto.

  • La stringa viene elaborata tramite un particolare MB che implementa l’interfaccia Welcome che definisce i metedi getWelcomeMessageLowercase() e getWelcomeMessageUppercase().
  • E’ possibile utilizzare Injection, Qualifiers, e Producers.
  • Provare a definire un producer per la stringa hello world e un producer per la stringa HELLO WORLD in maiuscolo specificando due diversi qualifiers.
  • Implementare una servlet che visualizza il messaggio hello world e HELLO WORLD in maiuscolo invocando i corrispondenti metodi dell’MB.

2. ⏯️ Esercizio Music Library primi 👣

Gestire una libreria musicale definendo un oggetto Song e un oggetto Library che è un MB che tramite l’utilizzo di Producers dichiara un ArrayList<Song>.

  • Il risultato dell’architettura deve permettere di utilizzare il seguente codice nella classe Library: @Inject ArrayList<Song> db;, preinizializzato con un fissato insieme di Song
    • la classe Library permette di gestire tutte le successive operazioni possibili di una libreria musicale (aggiungi, cancella, trova per ID, trova per nome, etc.).
  • Realizzare una serverlet che permette di visualizzare il contenuto della libreria in una pagina HTML.

3. Verifica del funzionamento di Safe Exam Browser

Approfondimenti

Oggetti Remoti con Java Socket

Esercizio 1

Sviluppare l’esempio di oggetti remoti con Java Socket, come descritto nel libro “Programmazione con Oggetti Distribuiti: Java RMI” (Capitolo 2).

Esercizio 2

Modificare l’esempio di oggetti remoti con Java Socket, come descritto nel libro “Programmazione con Oggetti Distribuiti: Java RMI” (Capitolo 2), in modo da poter gestire più client contemporaneamente. In particolare, il server deve essere in grado di gestire più richieste di connessione da parte dei client, creando un nuovo thread per ogni connessione accettata, gestendo le richieste in modo concorrente.

Esercizio 3

Modificare l’esempio di oggetti remoti con Java Socket, per la realizzazione della gestione di una libreria musicale. In particolare, il server deve essere in grado di gestire più richieste di connessione da parte dei client, creando un nuovo thread per ogni connessione accettata, gestendo le richieste in modo concorrente. Il server deve essere in grado di gestire le seguenti operazioni:

  • Aggiungere un nuovo brano musicale alla libreria;
  • Rimuovere un brano musicale dalla libreria;
  • Visualizzare la lista dei brani musicali presenti nella libreria;
  • Ricercare un brano musicale per titolo o per artista.
  • Ottenere il link al brano musicale per poterlo ascoltare in streaming.

Java Remote Method Invocation (RMI)

Hello World con Java Remote Method Invocation

  • In questo esempio utilizzeremo per il servizio di naming Java RMI registry rmiregistry presente nella directory bin della jdk,
    • per permettere al registro di caricare la codebase necessaria dobbiamo avviarlo all’interno della directory contente i file .classdel nostro progetto, nel costro caso la direcory $Project/build/classes
  • Per i nostri scopi definiamo un nuovo SecurityManager che garantisce una politica AllPermission.
  • Per la lookup degli oggetti remoti utilizzeremo la classe Naming.
  • Material di supporto: Oracle Getting Started Using Java RMI
  1. Definizione interfaccia remota Hello.java
   public interface Hello extends java.rmi.Remote {
     String dimmiQualcosa(String daChi) throws java.rmi.RemoteException;
   }
  1. Implementazione dell’oggetto remoto HelloImpl.java
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject; 
import java.security.Permission;
import java.util.logging.Logger;
public class HelloImpl extends UnicastRemoteObject implements Hello {
    
    private static final long serialVersionUID = -4469091140865645865L;
    static Logger logger= Logger.getLogger("global");
    public HelloImpl() throws RemoteException { }
    public String dimmiQualcosa(String daChi) throws RemoteException {
        logger.info("Sto salutando "+daChi);
        return "Ciao "+daChi+"!"; 
    }
    public static void main(String args[]) throws RemoteException { 
        //questo codice permette di evitare di scrivere il file policy
        System.setSecurityManager(new SecurityManager() {
            @Override
            public void checkPermission(Permission perm) {
            }
            @Override
            public void checkPermission(Permission perm, Object context) {
            }
        });
        try {
            logger.info("Creo l’oggetto remoto..."); 
            HelloImpl obj = new HelloImpl(); 
            logger.info("... ne effettuo il rebind...");
            Naming.rebind("HelloServer", obj); 
           
            logger.info("... Pronto!");
        } catch (Exception e) { 
            e.printStackTrace();
        }
    }
}
  1. Definizione del client che utilizza l’oggetto remoto HelloClient.java
import java.rmi.*;
import java.util.logging.Logger;
public class HelloClient {
    static Logger logger= Logger.getLogger("global");
    public static void main(String args[]) { 
        try {
            
        logger.info("Sto cercando l’oggetto remoto..."); 
        Hello obj = (Hello) Naming.lookup("rmi://localhost/HelloServer");  
        logger.info("... Trovato! Invoco metodo...");
        String risultato = obj.dimmiQualcosa("Pippo");
        System.out.println("Ricevuto:"+ risultato); } 
        catch (Exception e) { e.printStackTrace(); }

    }
}
  1. Esecuzione del progetto
    • avviare rmiregistry dalla directory ~/$PROJECT_NAME/build/classes porta di default rmiregistry 1099.
    • eseguire HelloImpl.javada netbeans tasto destro run file.
    • eseguire HelloClient.javada netbeans tasto destro run file.

rmiregistry locale e export di oggetti remoti

  • Server
public void createStubAndBind() throws RemoteException {
	MessengerService stub = (MessengerService) UnicastRemoteObject.exportObject((MessengerService) this, 0);
	Registry registry = LocateRegistry.createRegistry(1099);
	registry.rebind("MessengerService", stub);
}
  • Client
Registry registry = LocateRegistry.getRegistry();
MessengerService server = (MessengerService) registry.lookup("MessengerService");
responseMessage = server.sendMessage("Client Message");

Esercizi Java RMI

  1. Progettare e realizzare una semplice applicazione RMI client/server per realizzare una calcolatrice:
    • Interfaccia remota Calculator che definire i metodi per le operazioni di somma, sottrazione, divisione e moltiplicazione;
    • CalculatorImpl implementa l’interfaccia Calculator ed estende java.rmi.server.UnicastRemoteObject definendo la logica dell’applicazione;
    • CalculatorServer definisce solo il metodo main e costruisce ed esporta l’oggetto remoto CalculatorImpl;
    • CalculatorClient utilizza l’oggetto remoto Calculator.
  2. Progettare e realizzare una applicazione RMI client/server per la gestione delle prenotazioni di un Hotel, con le seguenti operazioni:
    • il client deve essere in grado di ottenere la lista delle stanze libere;
    • il client può tentare di prenotare una stanza e ottiene un risultato booleano;
    • la applicazione deve funzionare in presenza di più client connessi.
  3. Progettare e realizzare una applicazione RMI client/server per la gestione di un sistema di votazione, con le seguenti operazioni:
    • il client deve essere in grado di ottenere la lista dei candidati;
    • il client può votare per un candidato e ottiene lo score medio;
    • il client può ottenere il risultato delle votazioni (media delle votazioni);
    • la applicazione deve funzionare in presenza di più client connessi.
  4. Progettare e realizzare una applicazione RMI client/server (multi-client) per la gestione di una libreria musicale:
    • il client deve essere in grado di ottenere la lista delle canzoni (titolo univoco - link youtube - client name univoco);
    • il client può caricare una nuova canzone (titolo univoco - link youtube - client name univoco) visibile a tutti gli altri client;
    • il client deve essere in grado di creare delle playlist musicali (sub-set delle canzoni) con titolo univoco visibile a tutti gli altri client;
    • il client può eseguire il play di una canzone o di una playlist (il risultato del metodo sono tutti i link youtube);
    • il server tiene traccia della riproduzione di una specifica canzone o di una playlist (titolo univoco canzone/playlist, #volte, Lista delle riproduzioni con data e ora);
    • il server deve permettere al client di ottenere la lista delle TOP 10 canzoni/playlist riprodotte per ultima settimana, mese e anno.
    • Nota 🔗 : ottenere i link da youtube utilizzando il tasto Condividi ➡️ Incorpora dal player youtube;