[Only registered and activated users can see links. Click Here To Register...]
An sich kann man mit diesem Aufbau mit sehr wenig Aufwand verschiedenste Patchmethoden implementieren (z.B. Patching von einem Webserver, FTP, aus einer Datenbank (Blob-Feld), uvm.)
Vielleicht kann damit ja jemand etwas anfangen :)
Patcher:
PatchFileChecker zum Vergleich mit Checksummen:
Eine einfache Vererbung dazu mit SHA256 Hash:
Eine Implementierung des PatchExecuter Interfaces mit einem beliebigen Byte-Basiertem InputStream:
Code:
package com.dev.patcher_core;
import com.dev.common.error.Error;
import com.dev.common.error.ErrorManager;
import com.dev.common.property.Property;
import com.dev.common.property.ReadOnlyProperty;
import com.dev.common.threading.QueueThread;
import com.dev.common.threading.ThreadTask;
import com.dev.patcher_core.model.Patch;
import com.dev.patcher_core.model.PatchFile;
import com.dev.patcher_core.report.PatchFileReport;
import com.dev.patcher_core.report.PatchReport;
import com.dev.patcher_core.report.ReadOnlyPatchReport;
public class Patcher<T extends PatchFile>
{
private final PatchFileChecker<T> patchFileChecker;
private final PatchExecuter<T> patchExecuter;
private final QueueThread patchExecuterThread;
// patch properties
private final Property<ReadOnlyPatchReport<T>> patchReport;
public Patcher(PatchFileChecker<T> patchFileChecker, PatchExecuter<T> patchExecuter)
{
if (patchFileChecker == null || patchExecuter == null)
{
throw new IllegalArgumentException();
}
this.patchFileChecker = patchFileChecker;
this.patchExecuter = patchExecuter;
this.patchExecuterThread = new QueueThread(true);
this.patchReport = new Property<>();
}
public final ReadOnlyPatchReport<T> patch(Patch<? extends T> patch) throws IllegalPatchStateException, InterruptedException
{
if (this.patchExecuterThread.isAlive())
{
throw new IllegalPatchStateException("patch is already running");
}
// patch progress info
PatchReport<T> patchReport = new PatchReport<>(patch);
this.patchReport.set(patchReport.getReadOnlyPatchReport());
// patchvorgang
long patchSize = patch.getPatchSize();
boolean useFileCount = patchSize == -1;
if (useFileCount)
{
patchSize = patch.getPatchFiles().length;
}
for (T patchFile : patch.getPatchFiles())
{
long patchFileSize = 1;
if (!useFileCount)
{
patchFileSize = patchFile.getSourceFileSize();
}
double patchFileInfluenceInCompleteProgress = new Long(patchFileSize).doubleValue() / new Long(patchSize).doubleValue();
boolean isLocalFileUpToDate = false;
try
{
isLocalFileUpToDate = this.patchFileChecker.check(patchFile);
}
catch (Exception e)
{
ErrorManager.getInstance().errorOccured(new Error(e));
}
if (!isLocalFileUpToDate)
{
PatchFileReport<T> pfReport = new PatchFileReport<>(patchFile);
pfReport.progressProperty().getChangeEventList().addEvent((oldValue, newValue) -> {
double change = newValue - oldValue;
double changeInfluenceInCompleteProgress = change * patchFileInfluenceInCompleteProgress;
patchReport.progressProperty().set(patchReport.progressProperty().get() + changeInfluenceInCompleteProgress);
});
this.patchExecuterThread.appendQueue(new ThreadTask(
String.format("Executing patch for file %s", patchFile.getDestinationPath().toString()),
() -> {
patchReport.currentFileReportProperty().set(pfReport.getReadOnlyPatchFileReport());
try
{
this.patchExecuter.executePatch(pfReport);
patchReport.getUpdatedFiles().add(pfReport.getReadOnlyPatchFileReport());
}
catch (Exception e)
{
ErrorManager.getInstance().errorOccured(new Error(e));
pfReport.patchExceptionProperty().set(e);
patchReport.getUpdateFailedFiles().add(pfReport.getReadOnlyPatchFileReport());
}
patchReport.currentFileReportProperty().set(null);
}
));
}
else
{
patchReport.progressProperty().set(patchReport.progressProperty().get() + patchFileInfluenceInCompleteProgress);
patchReport.getNotUpdatedFiles().add(patchFile);
}
}
if (this.patchExecuterThread.isAlive())
{
this.patchExecuterThread.join();
}
this.patchReport.set(null);
return patchReport.getReadOnlyPatchReport();
}
public ReadOnlyProperty<ReadOnlyPatchReport<T>> patchReportProperty()
{
return this.patchReport.getReadOnlyProperty();
}
public PatchFileChecker<? extends T> getPatchFileChecker()
{
return this.patchFileChecker;
}
public PatchExecuter<? extends T> getPatchExecuter()
{
return this.patchExecuter;
}
}
Code:
package com.dev.patcher.common;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import com.dev.common.io.Checksum;
import com.dev.patcher_core.PatchFileChecker;
import com.dev.patcher_core.model.PatchFile;
public abstract class ChecksumPatchFileChecker<T extends PatchFile> implements PatchFileChecker<T>
{
public ChecksumPatchFileChecker()
{
}
public boolean check(T patchFile, String hashAlgo) throws NoSuchAlgorithmException, IOException
{
File dest = patchFile.getDestinationPath().toFile();
if (!dest.exists())
{
return false;
}
return Checksum.buildStringChecksum(dest, hashAlgo).equals(patchFile.getSourceChecksum());
}
}
Code:
package com.dev.patcher.common;
import com.dev.patcher_core.model.PatchFile;
public class SHA256PatchFileChecker<T extends PatchFile> extends ChecksumPatchFileChecker<T>
{
public SHA256PatchFileChecker()
{
super();
}
[MENTION=295804]Override[/MENTION]
public boolean check(T patchFile) throws Exception
{
return super.check(patchFile, "SHA-256");
}
}
Code:
package com.dev.patcher.common;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import com.dev.common.property.Property;
import com.dev.common.property.ReadOnlyProperty;
import com.dev.patcher_core.PatchExecuter;
import com.dev.patcher_core.model.PatchFile;
import com.dev.patcher_core.report.PatchFileReport;
public abstract class InputStreamPatchExecutor<T extends PatchFile> implements PatchExecuter<T>
{
private static final int UPDATE_SPEED_MILLIS = 250;
private final Property<Double> speed;
public InputStreamPatchExecutor()
{
this.speed = new Property<>(0.0);
}
protected void executePatch(PatchFileReport<T> patchFileReport, long contentSize, InputStream in) throws Exception
{
patchFileReport.progressProperty().set(0.0);
patchFileReport.completedSizeProperty().set(0l);
File dest = patchFileReport.getPatchFile().getDestinationPath().toFile();
if (dest.getParentFile() != null && !dest.getParentFile().exists() && !dest.getParentFile().mkdirs())
{
throw new Exception("Cannot create dirs for file");
}
FileOutputStream out = new FileOutputStream(dest);
int bytesRead = 0;
byte[] buffer = new byte[8192];
// speed calc
long lastSpeedUpdateTime = System.currentTimeMillis();
long bytesReadSinceLastUpdate = 0;
while ((bytesRead = in.read(buffer)) != -1)
{
out.write(buffer, 0, bytesRead);
patchFileReport.completedSizeProperty().set(patchFileReport.completedSizeProperty().get() + bytesRead);
bytesReadSinceLastUpdate += bytesRead;
long currentTime = System.currentTimeMillis();
long passedTime = currentTime - lastSpeedUpdateTime;
if (passedTime >= InputStreamPatchExecutor.UPDATE_SPEED_MILLIS)
{
this.speed.set(new Long(bytesReadSinceLastUpdate).doubleValue() / (new Long(passedTime).doubleValue() / 1000.0));
lastSpeedUpdateTime = currentTime;
bytesReadSinceLastUpdate = 0;
}
if (contentSize != -1)
{
patchFileReport.progressProperty().set(patchFileReport.completedSizeProperty().get().doubleValue() / new Long(contentSize).doubleValue());
}
}
patchFileReport.progressProperty().set(1.0);
try
{
in.close();
out.close();
}
catch (Exception e)
{
throw e;
}
}
[MENTION=295804]Override[/MENTION]
public ReadOnlyProperty<Double> speedProperty()
{
return this.speed.getReadOnlyProperty();
}
}
HTTPPatch:
HTTPPatchFile:
HTTPPatcher:
HTTPPatchExecuter:
Code:
package com.dev.patcher.common.http;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import com.dev.patcher_core.model.Patch;
public class HTTPPatch implements Patch<HTTPPatchFile>
{
private final String version;
private final long publishDate;
private final long patchSize;
private final HTTPPatchFile[] patchFiles;
private HTTPPatch()
{
this.version = "0.0";
this.publishDate = 0;
this.patchSize = -1;
this.patchFiles = null;
}
[MENTION=295804]Override[/MENTION]
public String getVersion()
{
return this.version;
}
[MENTION=295804]Override[/MENTION]
public LocalDateTime getPublishDate()
{
return LocalDateTime.ofEpochSecond(this.publishDate, 0, ZoneOffset.UTC);
}
[MENTION=295804]Override[/MENTION]
public long getPatchSize()
{
return this.patchSize;
}
[MENTION=295804]Override[/MENTION]
public HTTPPatchFile[] getPatchFiles()
{
return this.patchFiles;
}
}
Code:
package com.dev.patcher.common.http;
import java.nio.file.Path;
import java.nio.file.Paths;
import com.dev.patcher_core.model.PatchFile;
public class HTTPPatchFile implements PatchFile
{
private final String destinationPath;
private Path destinationPathAsObject;
private final String sourceChecksum;
private final long sourceFileSize;
private final String sourceURL;
private HTTPPatchFile()
{
this.destinationPath = null;
this.sourceChecksum = null;
this.sourceFileSize = -1;
this.sourceURL = null;
}
[MENTION=295804]Override[/MENTION]
public Path getDestinationPath()
{
if (this.destinationPathAsObject == null)
{
this.destinationPathAsObject = Paths.get(this.destinationPath);
}
return this.destinationPathAsObject;
}
[MENTION=295804]Override[/MENTION]
public String getSourceChecksum()
{
return this.sourceChecksum;
}
[MENTION=295804]Override[/MENTION]
public long getSourceFileSize()
{
return this.sourceFileSize;
}
public String getSourceURL()
{
return this.sourceURL;
}
[MENTION=295804]Override[/MENTION]
public String getSourceInformation()
{
return getSourceURL();
}
}
Code:
package com.dev.patcher.common.http;
import com.dev.patcher.common.SHA256PatchFileChecker;
import com.dev.patcher_core.Patcher;
import com.dev.patcher_core.report.ReadOnlyPatchReport;
public abstract class HTTPPatcher<T extends HTTPPatchFile> extends Patcher<T>
{
public HTTPPatcher()
{
super(
new SHA256PatchFileChecker<>(),
new HTTPPatchExecuter<>()
);
}
public abstract ReadOnlyPatchReport<HTTPPatchFile> patch() throws Exception;
}
Code:
package com.dev.patcher.common.http;
import java.net.HttpURLConnection;
import java.net.URL;
import com.dev.patcher.common.InputStreamPatchExecutor;
import com.dev.patcher_core.report.PatchFileReport;
public class HTTPPatchExecuter<T extends HTTPPatchFile> extends InputStreamPatchExecutor<T>
{
public HTTPPatchExecuter()
{
}
[MENTION=295804]Override[/MENTION]
public void executePatch(PatchFileReport<T> patchFileReport) throws Exception
{
HttpURLConnection conn = (HttpURLConnection) new URL(patchFileReport.getPatchFile().getSourceURL()).openConnection();
int responseCode = 0;
if ((responseCode = conn.getResponseCode()) != HttpURLConnection.HTTP_OK)
{
throw new Exception(String.format("Cannot download file (got http responsecode %d)", responseCode));
}
super.executePatch(patchFileReport, conn.getContentLengthLong(), conn.getInputStream());
conn.disconnect();
}
}
LocalPatch:
LocalPatchFile:
LocalPatcher:
LocalPatchExecuter:
Code:
package com.dev.patcher.common.local;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import com.dev.patcher_core.model.Patch;
public class LocalPatch implements Patch<LocalPatchFile>
{
private final String version;
private final long publishDate;
private final long patchSize;
private final LocalPatchFile[] patchFiles;
private LocalPatch()
{
this.version = "0.0";
this.publishDate = 0;
this.patchSize = -1;
this.patchFiles = null;
}
[MENTION=295804]Override[/MENTION]
public String getVersion()
{
return this.version;
}
[MENTION=295804]Override[/MENTION]
public LocalDateTime getPublishDate()
{
return LocalDateTime.ofEpochSecond(this.publishDate, 0, ZoneOffset.UTC);
}
[MENTION=295804]Override[/MENTION]
public long getPatchSize()
{
return this.patchSize;
}
[MENTION=295804]Override[/MENTION]
public LocalPatchFile[] getPatchFiles()
{
return this.patchFiles;
}
}
Code:
package com.dev.patcher.common.local;
import java.nio.file.Path;
import java.nio.file.Paths;
import com.dev.patcher_core.model.PatchFile;
public class LocalPatchFile implements PatchFile
{
private final String destinationPath;
private Path destinationPathAsObject;
private final String sourceChecksum;
private final long sourceFileSize;
private final String sourcePath;
private Path sourcePathAsObject;
public LocalPatchFile()
{
this.destinationPath = null;
this.sourceChecksum = null;
this.sourceFileSize = 0l;
this.sourcePath = null;
}
[MENTION=295804]Override[/MENTION]
public Path getDestinationPath()
{
if (this.destinationPathAsObject == null)
{
this.destinationPathAsObject = Paths.get(this.destinationPath);
}
return this.destinationPathAsObject;
}
[MENTION=295804]Override[/MENTION]
public String getSourceChecksum()
{
return this.sourceChecksum;
}
[MENTION=295804]Override[/MENTION]
public long getSourceFileSize()
{
return this.sourceFileSize;
}
public Path getSourcePath()
{
if (this.sourcePathAsObject == null)
{
this.sourcePathAsObject = Paths.get(this.sourcePath);
}
return this.sourcePathAsObject;
}
[MENTION=295804]Override[/MENTION]
public Path getSourceInformation()
{
return getSourcePath();
}
}
Code:
package com.dev.patcher.common.local;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.dev.common.gson.GsonUtils;
import com.dev.patcher.common.SHA256PatchFileChecker;
import com.dev.patcher_core.IllegalPatchStateException;
import com.dev.patcher_core.Patcher;
import com.dev.patcher_core.report.ReadOnlyPatchReport;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
public class LocalPatcher extends Patcher<LocalPatchFile>
{
private final File patchList;
public LocalPatcher(File patchList)
{
super(
new SHA256PatchFileChecker<>(),
new LocalPatchExecutor()
);
this.patchList = patchList;
}
public ReadOnlyPatchReport<LocalPatchFile> patch() throws JsonSyntaxException, JsonIOException, FileNotFoundException, IllegalPatchStateException, InterruptedException, IOException
{
return super.patch(GsonUtils.objectFromJsonInputStream(new FileInputStream(this.patchList), LocalPatch.class));
}
}
Code:
package com.dev.patcher.common.local;
import java.io.File;
import java.io.FileInputStream;
import com.dev.patcher.common.InputStreamPatchExecutor;
import com.dev.patcher_core.report.PatchFileReport;
public class LocalPatchExecutor extends InputStreamPatchExecutor<LocalPatchFile>
{
public LocalPatchExecutor()
{
}
[MENTION=295804]Override[/MENTION]
public void executePatch(PatchFileReport<LocalPatchFile> patchFileReport) throws Exception
{
File source = patchFileReport.getPatchFile().getSourcePath().toFile();
if (!source.exists())
{
throw new Exception("source file doesnt exist");
}
super.executePatch(patchFileReport, source.length(), new FileInputStream(source));
}
}
An sich kann man mit diesem Aufbau mit sehr wenig Aufwand verschiedenste Patchmethoden implementieren (z.B. Patching von einem Webserver, FTP, aus einer Datenbank (Blob-Feld), uvm.)
Vielleicht kann damit ja jemand etwas anfangen :)