2010-07-26 17 views
19

Sto cercando di incorporare un pezzo di codice che stamperà il momento in cui la classe corrente è stata compilata per l'ultima volta. Come può essere implementato in Java?Tempo di stampa Java dell'ultima compilazione

+0

Per quale motivo è la data di compilazione di interesse? –

+1

@MichaelKonietzka c'è un modo canonico per recuperare i numeri di versione di un JAR in fase di esecuzione? – uprego

risposta

11

Non v'è alcun supporto diretto per questo in Java, poiché non v'è alcuna preprocessore. L'equivalente più vicino è l'attributo "Build-Date" nel manifest JAR. Molti sistemi di build aggiungono questo attributo per impostazione predefinita o forniscono i mezzi per aggiungerlo.

È quindi possibile leggere il manifest del JAR in fase di esecuzione per ottenere la data. La risposta a questo SO question descrive come leggere i valori dal manifest JAR.

L'alternativa è utilizzare il filtro delle risorse per aggiungere la data in un file delle proprietà, che viene quindi letto in fase di esecuzione. Questo è abbastanza ad-hoc, non standard e se si hanno molti vasi, con tempi di compilazione diversi, questo diventa rapidamente difficile da gestire, a meno che non si possa tenerne conto in una parte comune di come vengono costruiti tutti i vasi.

+1

+1. Stavo cercando una proprietà manifest standard ma non l'ho trovata in tempo utile. –

4

creare uno script di shell che aggiorna il codice classe con il tempo di compilazione sostituendo segnaposti speciali:

public final class CompilationInfo 
{ 
    public static final String TIME = "$COMPILE_TIME"; 
} 

Per ulteriori informazioni, vedere this article.

+1

Ci piace fare di StackOverflow il proprio repository di informazioni. Potresti spiegare e dare un esempio qui, o almeno riassumere? –

+0

Ecco un link al messaggio che SO dovrebbe accettare: http://groups.google.com/group/comp.lang.java.help/msg/5177eeaea2ef9ad9?hl=it –

+0

Risolto ora, grazie. – spektom

1

Non conoscendo un modo standard per farlo, il mio suggerimento è simile al collegamento di spektom, ma sarebbe quello di aggiungere un file di proprietà al tuo jar che viene compilato dallo script di build (Ant ha un'attività incorporata per generare una proprietà file). Magari mettilo su /buildinfo.properties. Quindi creare una classe Java che esegue il polling di quel file di proprietà in fase di runtime.

In Formica, potrebbe essere simile a questo:

... 
<tstamp/>  
... 
<propertyfile file="${output.dir}/buildinfo.properties"> 
    <entry key="build.date" value="${TSTAMP}"/> 
</propertyfile> 

E poi corrispondente Java

public Date getBuildDate() { 
    Properties buildProps = new Properties(); 
    buildProps.load(getClass().getResourceAsStream("/buildinfo.properties")); 
    return someConversion(buildProps.getProperty("build.date")); 
} 
1

Si compia la tua procedura di costruzione creare un file di proprietà che contiene le informazioni di cui avete bisogno, e poi leggere le proprietà come risorsa nel codice

5

E 'un po' goffo, ma si potrebbe fare questo con il filtraggio Ant.

Pop il seguente metodo nella classe:

public static String timeBuilt(){ 
    return "Built at @[email protected] on @[email protected]"; 
} 

poi mettere il seguente nel file Ant.

<target name="get-time"> 
    <tstamp> 
     <format property="buildTime" pattern="HH:mm:ss" locale="en,UK"/> 
     <format property="buildDate" pattern="dd-MM-yyyy" locale="en,UK"/> 
    </tstamp> 
</target> 

<target name="copy-files" depends="get-time"> 
    <filter token="timeBuilt" value="${buildTime}"/> 
    <filter token="dateBuilt" value="${buildDate}"/> 
    <copy includeemptydirs="false" todir="build" filtering="true"> 
     <fileset dir="src"/> 
    </copy> 
</target> 

Questo copierà tutto nella directory "src" di "costruire" e in tal modo andrà a sostituire @ timeBuilt @ e @ @ dateBuilt con l'ora e la data del costruire, rispettivamente. Basta fare in modo che il target della build dipenda dai file di copia e dalla directory "build", non dalla directory "src".

Il vantaggio di sostituire il contenuto di un metodo statico è che questo opererà su una base per classe - se si dovessero prendere i file di classe prodotti e combinarli con altri file di classe creati in un altro momento, loro avrebbero saputo quando furono costruiti. I file di proprietà sono sensibili, ma a meno che non si disponga di più file di proprietà, si avrà solo un tempo di compilazione per il pacchetto nel suo complesso.

8

Dal momento che questo non è mai stato menzionato, tutti coloro che cercano di risolvere questo problema con ogni mezzo necessario, può trovare questo come un caso, ma hacky, soluzione:

new Date(new File(getClass().getClassLoader().getResource(getClass().getCanonicalName().replace('.', '/') + ".class").toURI()).lastModified())) 

Può non essere bella, e molto bene potrebbe non essere compatibile su altre piattaforme, ma questo è l'unico modo che ho trovato per capire la data di compilazione della classe corrente in Java nativo.

+0

mi piace; ai miei occhi, questo approccio non è un hack. –

12

Questa domanda ha avuto risposta molto tempo fa. Ma nel caso qualcuno oscilli da qui è una soluzione che funziona per me, simile a quello suggerito da Supah Fly ma che supporta jar e file.

private long classBuildTimeMillis() throws URISyntaxException, IllegalStateException, IllegalArgumentException { 
    URL resource = getClass().getResource(getClass().getSimpleName() + ".class"); 
    if (resource == null) { 
     throw new IllegalStateException("Failed to find class file for class: " + 
             getClass().getName()); 
    } 

    if (resource.getProtocol().equals("file")) { 

     return new File(resource.toURI()).lastModified(); 

    } else if (resource.getProtocol().equals("jar")) { 

     String path = resource.getPath(); 
     return new File(path.substring(5, path.indexOf("!"))).lastModified(); 

    } else { 

     throw new IllegalArgumentException("Unhandled url protocol: " + 
       resource.getProtocol() + " for class: " + 
       getClass().getName() + " resource: " + resource.toString()); 
    } 
} 

Ma questo non gestirà i file zip o un contesto statico e genera eccezioni invece di restituire null se le cose vanno a sud. Questo è un po 'più amichevole:

private static final Date buildDate = getClassBuildTime(); 

/** 
* Handles files, jar entries, and deployed jar entries in a zip file (EAR). 
* @return The date if it can be determined, or null if not. 
*/ 
private static Date getClassBuildTime() { 
    Date d = null; 
    Class<?> currentClass = new Object() {}.getClass().getEnclosingClass(); 
    URL resource = currentClass.getResource(currentClass.getSimpleName() + ".class"); 
    if (resource != null) { 
     if (resource.getProtocol().equals("file")) { 
      try { 
       d = new Date(new File(resource.toURI()).lastModified()); 
      } catch (URISyntaxException ignored) { } 
     } else if (resource.getProtocol().equals("jar")) { 
      String path = resource.getPath(); 
      d = new Date(new File(path.substring(5, path.indexOf("!"))).lastModified());  
     } else if (resource.getProtocol().equals("zip")) { 
      String path = resource.getPath(); 
      File jarFileOnDisk = new File(path.substring(0, path.indexOf("!"))); 
      //long jfodLastModifiedLong = jarFileOnDisk.lastModified(); 
      //Date jfodLasModifiedDate = new Date(jfodLastModifiedLong); 
      try(JarFile jf = new JarFile (jarFileOnDisk)) { 
       ZipEntry ze = jf.getEntry (path.substring(path.indexOf("!") + 2));//Skip the ! and the/
       long zeTimeLong = ze.getTime(); 
       Date zeTimeDate = new Date(zeTimeLong); 
       d = zeTimeDate; 
      } catch (IOException|RuntimeException ignored) { } 
     } 
    } 
    return d; 
} 
+3

Questa vecchia risposta non gestiva i file zip per qualcosa come un file di guerra distribuito su weblogic, ad esempio, e lanciava eccezioni invece di restituire null della data non può essere determinata, e non gestiva un contesto statico. Ma l'ho modificato per farlo :). – ggb667

0

Ecco la mia classe per rilevare il tempo di compilazione del programma Java. Utilizza anche il codice dalla risposta this.

import java.io.File; 
import java.io.IOException; 
import java.net.URISyntaxException; 
import java.net.URL; 
import java.nio.file.attribute.FileTime; 
import java.text.DateFormat; 
import java.util.Date; 
import java.util.Enumeration; 
import java.util.Locale; 
import java.util.jar.JarFile; 
import java.util.zip.ZipEntry; 
import java.util.zip.ZipFile; 

public class BuildDate 
{ 
    private static Date buildDate; 

    static 
    { 
     try 
     { 
      buildDate = setBuildDate(); 
     } catch (Exception exception) 
     { 
      exception.printStackTrace(); 
     } 
    } 

    public static String getBuildDate() 
    { 
     int style = DateFormat.FULL; 
     Locale locale = Locale.getDefault(); 
     DateFormat dateFormat = DateFormat.getDateInstance(style, locale); 
     DateFormat timeFormat = DateFormat.getTimeInstance(style, locale); 

     return dateFormat.format(buildDate) + " " + timeFormat.format(buildDate); 
    } 

    private static Date setBuildDate() throws Exception 
    { 
     if (ProgramDirectoryUtilities.runningFromIntelliJ()) 
     { 
      return getClassBuildTime(); 
     } else 
     { 
      return getNewestFileDate(); 
     } 
    } 

    private static Date getNewestFileDate() throws Exception 
    { 
     String filePath = ProgramDirectoryUtilities.getJARFilePath(); 
     File file = new File(filePath); 
     ZipFile zipFile = new ZipFile(file); 
     Enumeration entries = zipFile.entries(); 

     long millis = -1; 

     while (entries.hasMoreElements()) 
     { 
      ZipEntry entry = (ZipEntry) entries.nextElement(); 

      if (!entry.isDirectory()) 
      { 
       FileTime fileTime = entry.getLastModifiedTime(); 
       long currentMillis = fileTime.toMillis(); 

       if (millis < currentMillis) 
       { 
        millis = currentMillis; 
       } 
      } 
     } 

     return new Date(millis); 
    } 

    /** 
    * Handles files, jar entries, and deployed jar entries in a zip file (EAR). 
    * 
    * @return The date if it can be determined, or null if not. 
    */ 
    private static Date getClassBuildTime() throws IOException, URISyntaxException 
    { 
     Date date = null; 
     Class<?> currentClass = new Object() 
     { 
     }.getClass().getEnclosingClass(); 
     URL resource = currentClass.getResource(currentClass.getSimpleName() + ".class"); 
     if (resource != null) 
     { 
      switch (resource.getProtocol()) 
      { 
       case "file": 
        date = new Date(new File(resource.toURI()).lastModified()); 
        break; 
       case "jar": 
       { 
        String path = resource.getPath(); 
        date = new Date(new File(path.substring(5, path.indexOf("!"))).lastModified()); 
        break; 
       } 
       case "zip": 
       { 
        String path = resource.getPath(); 
        File jarFileOnDisk = new File(path.substring(0, path.indexOf("!"))); 
        try (JarFile jarFile = new JarFile(jarFileOnDisk)) 
        { 
         ZipEntry zipEntry = jarFile.getEntry(path.substring(path.indexOf("!") + 2));//Skip the ! and the/
         long zeTimeLong = zipEntry.getTime(); 
         date = new Date(zeTimeLong); 
        } 
        break; 
       } 
      } 
     } 
     return date; 
    } 
} 

classe Utility:

import java.io.File; 
import java.lang.invoke.MethodHandles; 
import java.net.URISyntaxException; 
import java.net.URLDecoder; 
import java.util.zip.ZipEntry; 
import java.util.zip.ZipFile; 

public class ProgramDirectoryUtilities 
{ 
    public static String getJARFilePath() throws URISyntaxException 
    { 
     return new File(MethodHandles.lookup().lookupClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getAbsolutePath(); 
    } 

    public static boolean runningFromJAR() 
    { 
     try 
     { 
      String jarFilePath = new File(MethodHandles.lookup().lookupClass().getProtectionDomain() 
        .getCodeSource() 
        .getLocation() 
        .getPath()). 
        toString(); 
      jarFilePath = URLDecoder.decode(jarFilePath, "UTF-8"); 

      try (ZipFile zipFile = new ZipFile(jarFilePath)) 
      { 
       ZipEntry zipEntry = zipFile.getEntry("META-INF/MANIFEST.MF"); 

       return zipEntry != null; 
      } 
     } catch (Exception exception) 
     { 
      return false; 
     } 
    } 

    public static String getProgramDirectory() 
    { 
     if (runningFromJAR()) 
     { 
      return getCurrentJARDirectory(); 
     } else 
     { 
      return getCurrentProjectDirectory(); 
     } 
    } 

    private static String getCurrentProjectDirectory() 
    { 
     return new File("").getAbsolutePath(); 
    } 

    private static String getCurrentJARDirectory() 
    { 
     try 
     { 
      return new File(MethodHandles.lookup().lookupClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParent(); 
     } catch (URISyntaxException exception) 
     { 
      exception.printStackTrace(); 
     } 

     return null; 
    } 

    public static boolean runningFromIntelliJ() 
    { 
     String classPath = System.getProperty("java.class.path"); 
     return classPath.contains("idea_rt.jar"); 
    } 
}