Skip to content

Instantly share code, notes, and snippets.

@TonyWhite
Created October 6, 2012 12:54
Show Gist options
  • Select an option

  • Save TonyWhite/3844853 to your computer and use it in GitHub Desktop.

Select an option

Save TonyWhite/3844853 to your computer and use it in GitHub Desktop.
JDaemonFileSync
/**
* La classe è un demone che effettua la sincronizzazione dei file
*
* @author Antonio Bianco
* creazione: 02/10/2012
* ultima modifica: 03/10/2012
*/
import java.io.File;
import java.nio.file.Files;
import static java.nio.file.StandardCopyOption.*;
import static java.lang.Thread.State.*;
import javax.swing.JOptionPane;
public class DaemonFileSync extends Thread
{
// Istanza di variabili
private boolean vivo;
private boolean loop;
/**
* Constructor for objects of class DaemonFileSync
*/
public DaemonFileSync()
{
super();
this.setDaemon(true);
vivo = true;
loop = true;
}
/**
* Corpo del demone
*/
public void run()
{
while(vivo)
{
while(loop)
{
try
{
Thread.sleep(Opzioni.getAttesa());
}
catch(Exception e){}
sincronizza();
}
}
}
/**
* Sincronizza i file
*/
private void sincronizza()
{
// long lastModified()
String descrizioneErrore = "";
for(int i=0; i<Opzioni.getFileSincronizzati(); i++)
{
//report+=i + " di " + Opzioni.getFileSincronizzati() + "\n";
descrizioneErrore = "";
File fileLocale = new File(Opzioni.getFileLocale(i));
File fileRemoto = new File(Opzioni.getFileRemoto(i));
boolean localePresente = fileLocale.exists();
boolean remotoPresente = fileRemoto.exists();
try
{
if ((localePresente)&&(remotoPresente)) // Esiste sia il file locale che remoto
{
/*
* I file sono accessibili sia in locale che in remoto:
* la sincronizzazione avviene in maniera normale
*/
// I file locali sono più aggiornati dei file remoti?
boolean sincronizzato = false;
if (fileLocale.lastModified()>(fileRemoto.lastModified()+Opzioni.getAttesa()))
{
descrizioneErrore = "Impossibile aggiornare i file remoti.";
Files.copy(fileLocale.toPath(), fileRemoto.toPath(), REPLACE_EXISTING);
sincronizzato = true;
}
else if (fileRemoto.lastModified()>(fileLocale.lastModified()+Opzioni.getAttesa()))
{
descrizioneErrore = "Impossibile aggiornare i file locali.";
Files.copy(fileRemoto.toPath(), fileLocale.toPath(), REPLACE_EXISTING);
sincronizzato = true;
}
if ((sincronizzato)&&(Opzioni.getNotifica(i)))
{
JOptionPane.showMessageDialog(null, "File sincronizzato correttamente:\n" + Opzioni.getFileLocale(i), Global.NOME_APPLICAZIONE, JOptionPane.INFORMATION_MESSAGE);
}
}
else if (localePresente) // Esiste solo il file locale
{
/*
* I file sono accessibili in locale, ma non in remoto:
* prova ad aggiornare i file remoti.
* Se non dovesse funzionare, significa che il percorso remoto è protetto oppure il dispositivo è disconnesso.
*/
descrizioneErrore = "Il percorso remoto è protetto oppure il dispositivo è disconnesso.";
Files.copy(fileLocale.toPath(), fileRemoto.toPath(), REPLACE_EXISTING);
}
else if (remotoPresente) // Esiste solo il file remoto
{
/*
* I file sono accessibili in remoto, ma non in locale:
* prova ad aggiornare i file locali.
* Se non dovesse funzionare, significa che non si hanno sufficienti permessi
*/
descrizioneErrore = "Non si hanno sufficienti permessi per scrivere i file in locale.";
Files.copy(fileRemoto.toPath(), fileLocale.toPath(), REPLACE_EXISTING);
}
else // Non esiste il locale e nemmeno il remoto
{
// Non fare niente
}
}
catch(Exception e)
{
System.out.println("WARNING! " + descrizioneErrore);
}
}
}
/**
* Ferma definitivamente il thread.
*/
public void ferma()
{
loop = false;
vivo = false;
while(true)
{
if (getState().equals(Thread.State.valueOf("TERMINATED")))
{
try
{
Thread.sleep(100);
break;
}
catch(Exception e){}
}
}
}
/**
* Mette in pausa il thread
*/
public void pausa()
{
loop = !loop; // Inverte il valore di una variabile booleana
}
}
/**
* La classe Global memorizza le variabili globali dell'applicazione.
*
* @author Antonio Bianco
* @version 02/10/2012
*/
import java.io.File;
import java.net.URI;
public class Global
{
// Istanzadi variabili
private static String absolutePath = null;
public static final String NOME_APPLICAZIONE = "JDaemonFileSync";
/**
* Constructor for objects of class Global
*/
public Global()
{
// initialise instance variables
}
/**
* Ritorna il percorso assoluto dell'applicazione.
* Se il file jar si trova in ~/Documenti/Programma/Applicazione.jar
* Il metodo restituisce ~/Documenti/Programma/
* Mantengo lo slash finale per favorire la concatenazione di altri nomi di file o cartelle.
*/
public static String getAbsolutePath()
{
if (absolutePath == null)
{
String path = Global.class.getProtectionDomain().getCodeSource().getLocation().getPath();
if (path != null)
{
File fileJar = new File(path);
String nomeJar = fileJar.getName();
String percorsoAssoluto = fileJar.getAbsolutePath();
if (!fileJar.isDirectory())
{
// Al percorso bisogna togliere il nome dell'eseguibile JAR
percorsoAssoluto = percorsoAssoluto.substring(0, percorsoAssoluto.length()-nomeJar.length());
}
if (!percorsoAssoluto.endsWith(System.getProperty("file.separator"))) // Finisce con un separatore di sistema?
{
// No: si aggiunge
percorsoAssoluto+=System.getProperty("file.separator");
}
//absolutePath = URI.create(percorsoAssoluto).getPath(); // Sostituisce i caratteri come %20 con i relativi ASCII, ma ho bug su Windows: mi riservo di correggerlo il prima possibile..
percorsoAssoluto = percorsoAssoluto.replace("%20", " "); // Nel frattempo uso questo.
absolutePath = percorsoAssoluto;
}
}
return absolutePath;
}
}
/**
* La classe JDaemonFileSync avvia il demone
*
* @author Antonio Bianco
* creazione: 02/10/2012
* ultima modifica: 03/10/2012
*/
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.lang.Process;
import java.lang.Runtime;
import java.net.URL;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
public class JDaemonFileSync
{
private static ThreadProcessoChiuso threadProcesso = null;
private static DaemonFileSync demone = null;
private static File opzioni;
private static final String nomeFileOpzioni = Global.getAbsolutePath()+"opzioni.txt";
/**
* Metodo di avvio
*/
public static void main(String args[])
{
setTema();
// Il file opzioni deve esistere
boolean errore = false;
String descrizioneErrore = "";
opzioni = new File(nomeFileOpzioni);
if (opzioni.exists())
{
//JOptionPane.showMessageDialog(null, "Il file " + nomeFileOpzioni + " esiste.", nomeApplicazione, JOptionPane.INFORMATION_MESSAGE);
try
{
descrizioneErrore = "Impossibile creare lo stream di comunicazione";
FileReader streamOpzioni = new FileReader(opzioni);
descrizioneErrore = "Impossibile accedere al buffer di lettura";
BufferedReader bufferOpzioni = new BufferedReader(streamOpzioni);
try
{
descrizioneErrore = "Il file è vuoto";
String bufAttesa = bufferOpzioni.readLine();
if (bufAttesa==null) Integer.parseInt("Forza errore"); // Metodo casareccio ma funzionante
descrizioneErrore = "Il file è corrotto: il primo campo non è un numero valido";
long attesa = Long.parseLong(bufAttesa);
Opzioni.setAttesa(attesa);
descrizioneErrore = "Il file è corrotto";
String bufComando = bufferOpzioni.readLine();
if (bufComando==null) Integer.parseInt("Forza errore"); // Metodo casareccio ma funzionante
Opzioni.setComando(bufComando);
descrizioneErrore = "Il file non contiene la lista di sincronizzazione";
String bufLocale = bufferOpzioni.readLine();
while(bufLocale!=null) // Esegue il ciclo solo se bufLocale non è null
{
// bufLocale contiene il nome del file locale
// bufRemoto contiene il nome del file remoto
// bufNotifica contiene il valore true o false per le notifica sui file
String bufRemoto = bufferOpzioni.readLine();
descrizioneErrore = "La lista dei file da sincronizzare è corrotta";
if (bufRemoto==null) Integer.parseInt("Forza errore"); // Metodo casareccio ma funzionante
String bufNotifica = bufferOpzioni.readLine();
descrizioneErrore = "La lista dei file da sincronizzare è corrotta";
boolean notifica = true;
if (bufNotifica.equals("true"))
{
notifica = true;
}
else if (bufNotifica.equals("false"))
{
notifica = false;
}
else
{
Integer.parseInt("Forza errore"); // Metodo casareccio ma funzionante
}
Opzioni.addFile(bufLocale, bufRemoto, notifica);
// Prova a leggere la riga seguente per ripetere il ciclo
bufLocale = bufferOpzioni.readLine();
}
// Genera un errore se la lista dei file da sincronizzare è vuota
descrizioneErrore = "Non esistono file da sincronizzare";
if (Opzioni.getFileSincronizzati()==0) Integer.parseInt("Forza errore"); // Metodo casareccio ma funzionante
}
catch(Exception exc)
{
errore = true;
JOptionPane.showMessageDialog(null, "ERRORE:\n" + descrizioneErrore, Global.NOME_APPLICAZIONE, JOptionPane.ERROR_MESSAGE);
}
descrizioneErrore = "Impossibile chiudere il buffer di lettura del file";
bufferOpzioni.close();
descrizioneErrore = "Impossibile chiudere lo stream di comunicazione del file";
streamOpzioni.close();
descrizioneErrore = "";
//DEBUG//JOptionPane.showMessageDialog(null, "File chiuso correttamente", nomeApplicazione, JOptionPane.INFORMATION_MESSAGE);
}
catch(Exception e)
{
errore = true;
JOptionPane.showMessageDialog(null, "ERRORE:\n" + descrizioneErrore, Global.NOME_APPLICAZIONE, JOptionPane.ERROR_MESSAGE);
}
if (errore)
{
JOptionPane.showMessageDialog(null, "Il programma si chiuderà a causa di un errore", Global.NOME_APPLICAZIONE, JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
else
{
// Controlla se i file da sincronizzare sono accessibili
if (!Opzioni.getAccessibilita())
{
int scelta = JOptionPane.showConfirmDialog(null,"ERRORE:\nI file da sincronizzare non sono accessibili,\ni contatti non saranno sincronizzati.\n\nVuoi avviare comunque l'applicazione?", Global.NOME_APPLICAZIONE, JOptionPane.YES_NO_OPTION);
if (scelta!=JOptionPane.YES_OPTION)
{
System.exit(1);
}
}
// LIBERO!!!!!!!!!!!!
}
}
else
{
// Avvia la finestra di configurazione
JOptionPane.showMessageDialog(null, "Il file " + nomeFileOpzioni + " non esiste.\nCrea il file di configurazione.", Global.NOME_APPLICAZIONE, JOptionPane.WARNING_MESSAGE);
System.exit(0);
}
// Avvia il comando
try
{
descrizioneErrore = "Impossibile avviare il comando\n" + Opzioni.getComando();
Process processo = Runtime.getRuntime().exec(Opzioni.getComando());
descrizioneErrore = "Impossibile creare il monitor per il processo";
threadProcesso = new ThreadProcessoChiuso(processo);
descrizioneErrore = "Impossibile monitorare il processo avviato";
threadProcesso.start();
}
catch(Exception e)
{
JOptionPane.showMessageDialog(null, "ERRORE:\n" + descrizioneErrore, Global.NOME_APPLICAZIONE, JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
// Avvia il demone
demone = new DaemonFileSync();
demone.start();
}
/**
* Esce dal programma fermando correttamente i thread
*/
public static void esci()
{
demone.ferma();
JOptionPane.showMessageDialog(null, "Sincronizzazione terminata.", Global.NOME_APPLICAZIONE, JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
/**
* Carica il tema predefinito.
*/
private static void setTema()
{
try
{
String classeTemaPredefinito = UIManager.getSystemLookAndFeelClassName();
UIManager.setLookAndFeel(classeTemaPredefinito);
// Correggi il FileChooser se il tema è GTK+
if ("GTK look and feel".equals(UIManager.getLookAndFeel().getName())) UIManager.put("FileChooserUI", "eu.kostia.gtkjfilechooser.ui.GtkFileChooserUI");
}
catch(Exception e)
{
System.err.println(e.getMessage());
}
}
}
/**
* La classe Opzioni mantiene le informazioni sulle opzioni del programma.
*
* @author Antonio Bianco
* creazione: 02/10/2012
* ultima modifica: 03/10/2012
*/
import java.io.File;
import java.util.Vector;
public class Opzioni
{
// Istanza di variabili
private static long attesa = 1000;
private static String comando = "";
private static Vector<String> fileLocali = new Vector<String>(); // File locali
private static Vector<String> fileRemoti = new Vector<String>(); // File remoti
private static Vector<Boolean> notifiche = new Vector<Boolean>(); // Notifiche
/**
* Constructor for objects of class Opzioni
*/
public Opzioni()
{
// Inizializzazione delle variabili
}
/**
* Aggiunge il file da sincronizzare
*/
public static void addFile(String fileLocale, String fileRemoto, boolean notifica)
{
fileLocali.add(fileLocale);
fileRemoti.add(fileRemoto);
notifiche.add(notifica);
}
/**
* Ritorna il tempo di attesa
*/
public static long getAttesa()
{
return attesa;
}
/**
* Ritorna il comando da avviare
*/
public static String getComando()
{
return comando;
}
/**
* Ritorna il file locale all'indice i della lista
*/
public static String getFileLocale(int i)
{
return fileLocali.get(i);
}
/**
* Ritorna il file remoto all'indice i della lista
*/
public static String getFileRemoto(int i)
{
return fileRemoti.get(i);
}
/**
* Ritorna true se al file dell'indice i è associata una notifica
*/
public static boolean getNotifica(int i)
{
return notifiche.get(i);
}
/**
* Ritorna il numero di file impegnati nella sincronizzazione
*/
public static int getFileSincronizzati()
{
return fileLocali.size();
}
/**
* Imposta il tempo di attesa in millisecondi tra una scansione e l'altra
*/
public static void setAttesa(long attesa)
{
Opzioni.attesa = attesa;
}
/**
* Imposta il comando da avviare
*/
public static void setComando(String comando)
{
Opzioni.comando = comando;
}
/**
* Ritorna true se tutti i file sono accessibili
*/
public static boolean getAccessibilita()
{
for(int i=0; i<fileLocali.size(); i++)
{
if ((!new File(fileLocali.get(i)).exists())||(!new File(fileRemoti.get(i)).exists()))
{
return false;
}
}
return true;
}
}
/**
* La classe chiude il programma quando il comando esterno viene terminato.
*
* @author Antonio Bianco
* creazione: 03/10/2012
* ultima modifica: 03/10/2012
*/
import java.lang.Process;
public class ThreadProcessoChiuso extends Thread
{
// Istanza di variabili
private Process processo;
/**
* Costruttore di oggetti della classe ThreadProcessoChiuso
*/
public ThreadProcessoChiuso(Process processo)
{
// Inizializza l'istanza di variabili
this.processo = processo;
}
/**
* Corpo del thread
*/
public void run()
{
try
{
processo.waitFor();
}
catch(Exception e){}
JDaemonFileSync.esci();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment