WIP
[hibernate4-maven-plugin] / src / main / java / de / juplo / plugins / hibernate / MD5ModificationTracker.java
diff --git a/src/main/java/de/juplo/plugins/hibernate/MD5ModificationTracker.java b/src/main/java/de/juplo/plugins/hibernate/MD5ModificationTracker.java
new file mode 100644 (file)
index 0000000..0349906
--- /dev/null
@@ -0,0 +1,239 @@
+package de.juplo.plugins.hibernate;
+
+
+import static de.juplo.plugins.hibernate.AbstractSchemaMojo.SCRIPT;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import org.apache.maven.plugin.logging.Log;
+
+
+
+/**
+ *
+ * @author Kai Moritz
+ */
+public class MD5ModificationTracker implements ModificationTracker
+{
+  private Map<String,String> properties;
+  private Map<String,String> classes;
+
+  private final Set<String> propertyNames;
+  private final Set<String> classNames;
+
+  private boolean modified = false;
+  private boolean failed = false;
+
+  private final File output;
+  private final File saved;
+  private final MessageDigest digest;
+  private final Log log;
+
+
+  MD5ModificationTracker(String buildDirectory, String filename, Log log)
+      throws
+        NoSuchAlgorithmException
+  {
+    propertyNames = new HashSet<String>();
+    classNames = new HashSet<String>();
+    output = new File(filename);
+    File tmp = new File(filename + ".md5s");
+    if (tmp.isAbsolute())
+    {
+      saved = tmp;
+    }
+    else
+    {
+      // Interpret relative file path relative to build directory
+      saved = new File(buildDirectory, tmp.getPath());
+      log.debug("Adjusted relative path, resulting path is " + saved.getPath());
+    }
+    digest = java.security.MessageDigest.getInstance("MD5");
+    this.log = log;
+  }
+
+
+  private String calculate(InputStream is)
+      throws
+        IOException
+  {
+    byte[] buffer = new byte[1024*4]; // copy data in 4MB-chunks
+    int i;
+    while((i = is.read(buffer)) > -1)
+      digest.update(buffer, 0, i);
+    is.close();
+    byte[] bytes = digest.digest();
+    BigInteger bi = new BigInteger(1, bytes);
+    return String.format("%0" + (bytes.length << 1) + "x", bi);
+  }
+
+  private boolean check(Map<String,String> values, String name, String value)
+  {
+    if (!values.containsKey(name) || !values.get(name).equals(value))
+    {
+      values.put(name, value);
+      return true;
+    }
+    else
+      return false;
+  }
+
+
+  @Override
+  public boolean track(String name, InputStream is) throws IOException
+  {
+    boolean result = check(classes, name, calculate(is));
+    classNames.add(name);
+    modified |= result;
+    return result;
+  }
+
+  @Override
+  public boolean check(String name, String property)
+  {
+    propertyNames.add(name);
+    return check(properties, name, property);
+  }
+
+  @Override
+  public boolean track(String name, String property)
+  {
+    boolean result = check(name, property);
+    modified |= result;
+    return result;
+  }
+
+  @Override
+  public boolean track(Properties properties)
+  {
+    boolean result = false;
+    for (String name : properties.stringPropertyNames())
+      result |= track(name, properties.getProperty(name));
+    return result;
+  }
+
+  @Override
+  public void track() throws IOException
+  {
+    if (output.exists())
+      track(SCRIPT, new FileInputStream(output));
+    else
+      track(SCRIPT, ZonedDateTime.now().toString());
+  }
+
+
+  @Override
+  public void touch()
+  {
+    modified = true;
+  }
+
+  @Override
+  public boolean modified()
+  {
+    for (String property : new HashSet<String>(properties.keySet()))
+      if (!propertyNames.contains(property))
+      {
+        modified = true;
+        properties.remove(property);
+      }
+     for (String clazz : new HashSet<String>(classes.keySet()))
+      if (!classNames.contains(clazz))
+      {
+        modified = true;
+        classes.remove(clazz);
+      }
+    return modified;
+  }
+
+
+  @Override
+  public void failed()
+  {
+    failed = true;
+  }
+
+
+  @Override
+  public void load()
+  {
+    if (saved.isFile() && saved.length() > 0)
+    {
+      try
+      {
+        FileInputStream fis = new FileInputStream(saved);
+        ObjectInputStream ois = new ObjectInputStream(fis);
+        properties = (HashMap<String,String>)ois.readObject();
+        classes = (HashMap<String,String>)ois.readObject();
+        ois.close();
+      }
+      catch (Exception e)
+      {
+        properties = new HashMap<String,String>();
+        classes = new HashMap<String,String>();
+        log.warn("Cannot read md5s from saved: " + e);
+      }
+    }
+    else
+    {
+      properties = new HashMap<String,String>();
+      classes = new HashMap<String,String>();
+      try
+      {
+        saved.createNewFile();
+      }
+      catch (IOException e)
+      {
+        log.debug("Cannot create file \"" + saved.getPath() + "\" for md5s: " + e);
+      }
+    }
+  }
+
+  @Override
+  public void save()
+  {
+    if (failed)
+    {
+      saved.delete();
+      return;
+    }
+
+    if (!modified)
+      return;
+
+    /** Write md5-sums for annotated classes to file */
+    try
+    {
+      FileOutputStream fos = new FileOutputStream(saved);
+      ObjectOutputStream oos = new ObjectOutputStream(fos);
+      oos.writeObject(properties);
+      oos.writeObject(classes);
+      oos.close();
+      fos.close();
+    }
+    catch (Exception e)
+    {
+      log.error("Cannot write md5-sums to file: " + e);
+    }
+  }  
+
+
+  @Override
+  public void turncat() throws IOException
+  {
+    new FileOutputStream(output).getChannel().truncate(0).close();
+  }
+}