Giter Site home page Giter Site logo

update4j's Introduction

update4j-logo

Build Status Apache License Java-9+ Maven Release Gitter

Read the documentation, explore the JavaDoc, or see it in action

Create a framework: design the environment and lifecycle (—bootstrap) to make your own auto-update framework and hack it to the core, or use the built-in default bootstrap.

Screenshots

Headless

Using the default bootstrap, downloads 4 files then launches hello-world.jar. You can see that subsequent runs won't download again.

headless

JavaFX

Using a custom bootstrap implemented to report progress in JavaFX, downloads 4 files then launches hello-world.jar.

javafx

Overview

Update4j is the first auto-update and launcher library designed for Java 9+. Easily host your application files anywhere (even Google Drive, Dropbox, Amazon S3, or Maven Central) and you can synchronize them with all your distributed applications. You can use any protocol you wish to retrieve those files and may be protected under authenticated API.

In update4j you have ultimate control of every process, from startup - update - launch - shutdown, since it's a library (you call the 3rd party code) not a framework (3rd party calls your code outside your control). In addition, every single piece of code is completely updatable; even update4j itself, once a new version is released! (Well, if you properly set up the environment.)

Installation & Usage

You can download or install using Maven:

<dependency>
    <groupId>org.update4j</groupId>
    <artifactId>update4j</artifactId>
    <version>1.5.9</version>
</dependency>

You can use it as a regular dependency, or you may run it as a runnable JAR file.

To run it in the modulepath, use either of:

$ java -p update4j-1.5.9.jar -m org.update4j
$ java -p . -m org.update4j

To run it in the classpath, use either of:

$ java -jar update4j-1.5.9.jar
$ java -cp * org.update4j.Bootstrap

For more information refer to Starting the Application in the wiki.

What's New in 1.5.x — Migration Guide

  • New update model Configuration.update(ArchiveUpdateOptions), using an Archive to store update files, it can then be 'installed' (calling Archive::install). #76
  • Deprecated previous update models, but still available for smooth migration.
  • Improved update return value as UpdateResult. #87
  • Using the DefaultLauncher, not passing default.launcher.main.class will run the command-line arguments as a script. #88
  • ignoreBootConflict no longer required if there are no user modules on the boot module layer.
  • DefaultBootstrap::updateFirst now performs update in parallel while launching the business app. #104
  • Support Elliptic Curve cipher. #89
  • Clamp update handler frac values between 0 and 1. #106

Sponsors

Basil Inc

License

This project is licensed under the Apache Software License 2.0

update4j's People

Contributors

debei avatar maertuerer avatar maths22 avatar meblum avatar micheljung avatar mordechaim avatar nkausche avatar vincent-dm avatar xzel23 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

update4j's Issues

bat2exe integrity

This is not really about update4j, but here it goes anyways.

I don't know if it is generally accepted or not but I find it disturbing that the website of the bat2exe tool you are offering in Wiki is mining cryptocurrency in visitors browser (or at least trying to, using coinhive.com captcha miner). Also since it is closed software and and there can be found quite many concerned statements about resulting exes containing malicius software, I think you should considering removing the tool reference from wiki. Some talk here: https://community.norton.com/en/forums/bat2exe

If you know better let me know, but I'm not brave enough to download such a tool and run any exes produced by it.

Using certificate with default Bootstrap

Certificate part work wonderfully when I make a key pair and use private key for building conf and use custom Delegate and do the following:

KeyFactory kf = KeyFactory.getInstance("DSA");
X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(Base64.getDecoder().decode("BASE64PUBLICKEY"));
DSAPublicKey key = (DSAPublicKey) kf.generatePublic(keySpecX509);
config.update(key)

However, if I want to use --cert=[path] and the default delegate I find it really difficult to build the certification file. I have tried so many different ways to create the file and I always end up getting an error like "Caused by: java.io.IOException: Short read of DER length" or "java.security.cert.CertificateException: Could not parse certificate: java.io.IOException: Incomplete data"

So I'm asking is there a simple way to generate a valid certification file with correct info to build the public key?

Automatic module name exception

After updating to 1.2.2, I've run into this while launching my app:

java.lang.IllegalStateException: Automatic module name 'jackson.module.jaxb.annotations' for file 'jackson-module-jaxb-annotations-2.8.6.jar' is not valid.
	at org.update4j.Configuration.checkBootConflicts(Configuration.java:1151)
	at org.update4j.Configuration.validateFile(Configuration.java:1113)
	at org.update4j.Configuration.updateImpl(Configuration.java:988)
	at org.update4j.Configuration.update(Configuration.java:848)
	at org.update4j.Configuration.update(Configuration.java:844)
	at org.update4j.Configuration.update(Configuration.java:832)
	at org.musetest.ide.launcher.UpdateApp.run(UpdateApp.java:27)
	at java.base/java.lang.Thread.run(Thread.java:844)

Looking at the code, it looks like this is because 'module' is a reserved word when naming modules. This jar (jackson-module-jaxb-annotations-2.8.6.jar) is being loaded onto the classpath. In fact, at the moment, I am loading everything on the classpath. When using 1.1.3, this was not an issue. Should this check be made in this case?

That library name is obviously out of my control :(

Null pointer on missing local updates.xml

"../jre-10.0.2/bin/java" -p update/ -m org.update4j --remote=https://www.URLCENSORED.com/updates.xml --local="C:\Program Files (x86)\SomeFolder\updates.xml" --stopOnUpdateError --syncLocal --stopOnUpdateError --singleInstance --cert="C:\Program Files (x86)\SomeFolder\SomeProgramFolder\updatevalidator.cer"
Exception in thread "main" java.lang.NullPointerException
at [email protected]/org.update4j.Configuration.getOldFiles(Configuration.java:1562)
at [email protected]/org.update4j.Configuration.deleteOldFiles(Configuration.java:1508)
at [email protected]/org.update4j.Configuration.deleteOldFiles(Configuration.java:1433)
at [email protected]/org.update4j.service.DefaultBootstrap.updateFirst(DefaultBootstrap.java:202)
at [email protected]/org.update4j.service.DefaultBootstrap.main(DefaultBootstrap.java:98)
at [email protected]/org.update4j.Bootstrap.start(Bootstrap.java:178)
at [email protected]/org.update4j.Bootstrap.start(Bootstrap.java:167)
at [email protected]/org.update4j.Bootstrap.main(Bootstrap.java:98)

I get the preceding error if the local copy of updates.xml does not exist yet, updater will create it and next run is fine. Is it supposed to work like so? I think if local copy does not exist update4j should create it and continue with update if the online version is found.

Feature request: Provide default launcher - so not required as Application dependency

The requirement to implement the Launcher service in the App is messy, IMO because it makes the App depend on update4j. The App does not need update4j - it runs just fine without it. But now I have to introduce a dependency just so it can be launched from update4j. If I do not own the App, this could be a challenge. It is an artificial dependency and is a bad thing (again...in my opinion).

I would like to see an alternative option: that just requires the classname and arguments. For example, implement a "LaunchSpec" interface (in the jar with the Delegate, update listener, etc) that provides the class name that should be started (via main) and a list of arguments to pass. Or maybe even get the classname from the Configuration. This way, the App does not need to depend on update4j. Only the installer/updater/launcher does - which is the way it should be (in my opinion).

Thanks for considering this.
Chris

URI and Path resolution fails.

Library uri and path resolution fails if the config lists them using a property and both the library and the property are os specific and it's not the current os.

Since the property doesn't get resolved it will just fail to create the URI or the Path.

I'm currently working on this.

Classpath for java8 jars & --add-exports/--add-opens

I have a complex application with some libraries, that are on the classpath and become a unnamed module. Also I have some automatic modules. This scenario leads to some problems.

As far as I see, the libraries with attribute modulepath="false" are not loaded. Only the ones with modulepath="true" are loaded ( in Configuration.launch() ) . Am I wrong? I tried to create a new URLClassLoader, but did not succeed yet.

As a second issue, i have to add exports and opens instructions. With the usual client I would add the parameter "--add-exports" and "--add-opens" directives. How can I achieve that with update4J? Using the ModuleLayer which is in the LaunchContext and trying to add it by code leads to e.g. java.lang.IllegalCallerException: module MyModule != module java.base .

Configuration.Builder Write Method

After reading the wiki, could´nt use the library.
Could explain better how to save the generated config file?

Example says:
Configuration.Builder cb = Configuration.withBase(URI.create("https://example.com/"),
Paths.get("/home/myapp/")).........
when its cb.builded.

Then for save you are saying:
Writer out = Files.newBufferedWriter(Paths.get("config.xml"));
config.write(out);

But its config its a Configuration Object and in Configuration.Builder there is no Write method!
so isnt possible to make cb.write(out)

How do we update bootstrap libraries

We currently have a setup like this. User downloads our installer/update and install it to:

/home/user/app/myapp01/ containing the following libs:
lib/update4j-1.2.2.jar
lib/myapp-boot.jar
myapp.ini
myapp.exe

When launching the app, an update will be performed and 200+ libs installed to:
/home/user/.myapp/<env.key>/lib/client/

But how can we update the update4j, myapp-boot.jar, myapp.ini and myapp.exe in the installation folder (that are currently in use)?
Can we just update them or is there risk of file locks?

How can we work with relative instead of absolute paths in such a case?
I did some testing rewriting the absolute path on the client side using updateMapper (in the bootstrap class on the client side). Is there a better way?

Request: Demo application code

I would like to try update4j, but the current documentation is not enough to know where to start.

A demo application + launcher code (like fxlauncher) for each way to use the library would go a long way to drive adoption of this library.

Sign config

Issue

The content of the config itself is not protected by any signature. So all properties may be modified by an attack. Especially the MainClass to execute may be modified to execute any arbitrary main class in a jer file on the disk.

Solution

See discussion in #26

  • Add a extra signature file, that holds the signature of the config file
  • Add a signature attribute to the XML root element, that holds the signature of the content of the root element

Consider exporting package org.update4j.util

I need to pass arguments to my business application that cannot be part of the config.xml (like access tokens, which are retrieved during bootstrapping). So, to pass data to my own implementation of Launcher, it seems I need to have my own implementation of Delegate as well.

I am currently writing my own Delegate service. In most ways it should behave similarly to the DefaultBootstrap, but parsing command line arguments without external libraries is hard. Luckily, you are providing the class ArgUtils, and it is even public! Yay! But wait a minute... I cannot access the class, because it is not exported by module-info.java.

This was probably a conscious design decision by you, but still, these are handy tools that are almost always helpful when developing a custom Delegate. Maybe some of these classes should be exported?

Should 'Library' be renamed?

The Library class represents any arbitrary file that will be managed by the framework. A Library can be anything from a real library, like a jar file or just a resources, as a logo or audio clip.

The name "library" was originally chosen instead of "file" to prevent conflicts with java.io.File, and following the precedent from FXLauncher using the name LibraryFile.

But the name has since fallen out of favor as it might cause a misconception that it may only include jar files, I thought about renaming and educating how to migrate.

I thought about Artifact instead, what do you think?

the <library> in the config xml file could still silently be supported but new code should only produce the new name.

Thread.currentThread().getContextClassLoader() cannot resolve classes that were loaded

First of all congratulations for your idea. It is great and has all I need to replace my old webstart clients with a new update system (java webstart will be canceled in java 11!).

But I have problems with my JavaFX Application. Whenever Thread.currentThread().getContextClassLoader()
is called (e.g. Application.launch() or FXMLLoader.getDefaultClassLoader()) a Classloader is returned, that does not include the classes of the loaded modules.

For the Application I can get rid of that problem by loading the class with the classloader that can be found by LaunchContext.getModuleLayer().

Isn't there a better way to set the context class loader of the thread?

UpdateHandler (with gui) breaks main application

I just switched to a custom UpdateHandler which contain some GUI/Swing code to display update status basically.

But once it is time to start the business application (spring based), some parts break. Spring xml files which normally loads by our business application cannot be found as classpath resources.

I have switched back and forth in the UpdateHandler and just adding a "new JPanel()" will break our application.

I have tried starting using via our own class and loading the config there as well as using org.update4J.Bootstrap and parameter --remote=http://....

It must be valid to have a small JFrame update in the update handler right?

The bootstrap classpath only consist of 2 jars, update4j and our custom bootstrap lib.

Trouble updating the main app jar

I don't understand what I am doing wrong, so any help you can offer would be much appreciated.

The main of my app is in fxtest.jar. My Launcher (in UpdateLauncher.jar) simply calls a static method in fxtest to start a trivial JavaFX app. When starting the app, I am running this in a folder with all the JARs and the Configuration file (update-release.xml):

java -classpath "./*" mypackage.Bootstrap

Bootstrap.main() does this:

        File file = new File("update-release.xml");
        FileInputStream instream = new FileInputStream(file);
        Configuration config = Configuration.read(new InputStreamReader(instream));
        config.update();
        config.launch();

If I try to start and the fxtest.jar is not already there, the launch fails:

fxtest-0.1.jar:  UPDATE
guava-23.0.jar:  SYNCHRONIZED
j2objc-annotations-1.1.jar:  SYNCHRONIZED
javax.activation-1.2.0.jar:  SYNCHRONIZED
jaxb-api-2.3.0.jar:  SYNCHRONIZED
jaxb-core-2.3.0.jar:  SYNCHRONIZED
jaxb-impl-2.3.0.jar:  SYNCHRONIZED
jsr305-1.3.9.jar:  SYNCHRONIZED
update4j-1.1.3-beta.jar:  SYNCHRONIZED
UpdaterLauncher.jar:  SYNCHRONIZED
Downloading: fxtest-0.1.jar <https://museide.s3.amazonaws.com/updates/fxtest-0.1.jar> (100%)
>> Launch
>> Starting App
Exception in thread "Thread-0" java.lang.NoClassDefFoundError: myapp/App
        at mypackage.Launcher.run(Launcher.java:15)
        at org.update4j.Configuration.lambda$launch$8(Configuration.java:529)
        at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.ClassNotFoundException: myapp.App
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
        ... 3 more

The fxtest.jar download succeeds, but the app does not, obviously, start. The ">> Starting App" message comes from my Launcher implementation, right before it makes the call into my app in fxtest.jar.

If I run it again, it runs fine. But, then, if I change fxtest.jar again and update the Configuration file, the next run fails on the update, and then the old version of the app is started.

>> Update
fxtest-0.1.jar:  UPDATE
guava-23.0.jar:  SYNCHRONIZED
j2objc-annotations-1.1.jar:  SYNCHRONIZED
javax.activation-1.2.0.jar:  SYNCHRONIZED
jaxb-api-2.3.0.jar:  SYNCHRONIZED
jaxb-core-2.3.0.jar:  SYNCHRONIZED
jaxb-impl-2.3.0.jar:  SYNCHRONIZED
jsr305-1.3.9.jar:  SYNCHRONIZED
update4j-1.1.3-beta.jar:  SYNCHRONIZED
UpdaterLauncher.jar:  SYNCHRONIZED
Downloading: fxtest-0.1.jar <https://museide.s3.amazonaws.com/updates/fxtest-0.1.jar> (100%)

java.nio.file.FileSystemException: D:\myapp\fxtest-0.1.jar: The process cannot access the file because it is being used by another process.

        at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:92)
        at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
        at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
        at java.base/sun.nio.fs.WindowsFileCopy.move(WindowsFileCopy.java:373)
        at java.base/sun.nio.fs.WindowsFileSystemProvider.move(WindowsFileSystemProvider.java:288)
        at java.base/java.nio.file.Files.move(Files.java:1413)
        at org.update4j.Configuration.updateImpl(Configuration.java:354)
        at org.update4j.Configuration.update(Configuration.java:195)
        at org.update4j.Configuration.update(Configuration.java:183)
        at org.update4j.Configuration.update(Configuration.java:175)
        at mypackage.Bootstrap.main(Bootstrap.java:20)
>> Launch
>> Starting App

This is my Configuration file (update-release.xml):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<!-- Generated by update4j. Licensed under Apache Software License 2.0 -->
<configuration timestamp="2018-08-05T17:45:24.481591400Z">
    <base uri="https://museide.s3.amazonaws.com/updates/" path="D:/myapp"/>
    <properties>
        <property key="app.name" value="My App"/>
    </properties>
    <libraries>
        <library path="animal-sniffer-annotations-1.14.jar" checksum="d80dae3" size="3482" classpath="true"/>
        <library path="commons-math3-3.6.1.jar" checksum="c2c7db79" size="2213560" classpath="true"/>
        <library path="error_prone_annotations-2.0.18.jar" checksum="4419104e" size="12078" classpath="true"/>
        <library path="fxtest-0.1.jar" checksum="c5b331cc" size="2615" classpath="true"/>
        <library path="guava-23.0.jar" checksum="f3a66fb9" size="2614708" classpath="true"/>
        <library path="j2objc-annotations-1.1.jar" checksum="26a6db22" size="8764" classpath="true"/>
        <library path="javax.activation-1.2.0.jar" checksum="a5461beb" size="78030" classpath="true"/>
        <library path="jaxb-api-2.3.0.jar" checksum="484d77c4" size="125632" classpath="true"/>
        <library path="jaxb-core-2.3.0.jar" checksum="cdef1be4" size="255502" classpath="true"/>
        <library path="jaxb-impl-2.3.0.jar" checksum="e09c9d49" size="963660" classpath="true"/>
        <library path="jsr305-1.3.9.jar" checksum="d24af926" size="33015" classpath="true"/>
        <library path="update4j-1.1.3-beta.jar" checksum="b42905ab" size="62241" classpath="true"/>
        <library path="UpdaterLauncher.jar" checksum="ba4f8869" size="3563" classpath="true"/>
    </libraries>
</configuration>

TIA!
Chris

Resolve properties that are defined while bootstrapping

I just want to know if I am missing something:

As far as I understand, properties inside the config.xml are resolved in three ways:

  1. By using properties that are defined inside the config.xml ifself (<properties>).
  2. By using system properties.
  3. By using system environment variables.

I want to use a placeholder for the base-uri, because we deploy the application (and the config.xml with it) to many different servers and I want to avoid building server-specific config-files and packages that contain them. The bootstrap application will provide the actual data to resolve the base-uri.

Unfortunately, Configuration.builder() doesn't even allow me to use a placeholder inside the base-uri without also specifying the property. This means the base-uri must be known at build-time.

The only solution I can think of is to use a dummy base-uri (like https://DUMMY_HOST/) for the config.xml. While bootstrapping I need to replace this dummy with the actual host by using a custom UpdateHandler. Doable, but maybe there is a nicer way?

Maybe it would be better to not enforce the resolvability of properties at build-time. Then, you could offer an overload of Configuration.read(...) that also accepts a map of bootstrap-time properties.

What do you think?

Also, I am sorry for "spamming" you with issues today. My product is almost completed, so I am sure you will here less of me in the future :)

Please build distribution with sources

It would be great if you could build your distribution with sources. For someone trying to trying to implement this for the first time, having the source seamlessly available from within my IDE would be really helpful. Building with JavaDocs would be even better :)

I know with Gradle it is just a few lines in the build. Maven is long forgotten for me, though.

TIA!

problem with java 11

I'm migrating from Java 9 to Java 11 and now I get:

Exception in thread "main" java.lang.module.FindException: Module jdk.jsobject not found, required by javafx.web
at java.base/java.lang.module.Resolver.findFail(Resolver.java:877)
at java.base/java.lang.module.Resolver.resolve(Resolver.java:191)
at java.base/java.lang.module.Resolver.resolve(Resolver.java:140)
at java.base/java.lang.module.Configuration.resolveAndBind(Configuration.java:482)
at java.base/java.lang.module.Configuration.resolveAndBind(Configuration.java:288)
at [email protected]/org.update4j.Configuration.launchImpl(Configuration.java:1242)
at [email protected]/org.update4j.Configuration.launch(Configuration.java:1200)
at [email protected]/org.update4j.service.DefaultBootstrap.updateFirst(DefaultBootstrap.java:201)
at [email protected]/org.update4j.service.DefaultBootstrap.main(DefaultBootstrap.java:103)
at [email protected]/org.update4j.Bootstrap.start(Bootstrap.java:271)
at [email protected]/org.update4j.Bootstrap.start(Bootstrap.java:254)
at [email protected]/org.update4j.Bootstrap.start(Bootstrap.java:167)
at [email protected]/org.update4j.Bootstrap.main(Bootstrap.java:110)

I have included javafx-sdk-11.0.1 in the business app and I'm adding all the jars to modulepath in configuration.

When I launch business app without update4j I specify "javafx-sdk-11.0.1\lib" folder to modulepath and everything runs smoothly.

Any idea why java.base -> jdk.jsobject is not found when launching with update4j and how to fix it?

Unknown command ' -jar'.

Starting in delegate with launch4j with exact same command that worked on 1.2.2.
jvm options: -p update/update4j-1.3.3.jar -m org.update4j -jar "C:\pathto\update4j-1.3.3.jar"

On 1.3.3 i get:

Exception in thread "main" java.lang.IllegalArgumentException: Unknown command '-jar'. at [email protected]/org.update4j.util.ArgUtils.parseArgs(ArgUtils.java:57) at [email protected]/org.update4j.Bootstrap.main(Bootstrap.java:85)

So this is because of new ArgUtils. Could you also allow -jar without throwing IllegalArgumentException?

Also if I use it like suggested on the wiki without module specified:
$ java -p modulepath/ -jar update4j<version>.jar

I get:

Exception in thread "main" java.lang.IllegalStateException: No provider found for org.update4j.service.Launcher at org.update4j.service.Service.loadService(Service.java:52) at org.update4j.Configuration.launchImpl(Configuration.java:1332) at org.update4j.Configuration.launch(Configuration.java:1236) at org.update4j.Configuration.launch(Configuration.java:1224) at org.update4j.service.DefaultBootstrap.updateFirst(DefaultBootstrap.java:208) at org.update4j.service.DefaultBootstrap.main(DefaultBootstrap.java:98) at org.update4j.Bootstrap.start(Bootstrap.java:178) at org.update4j.Bootstrap.start(Bootstrap.java:167) at org.update4j.Bootstrap.main(Bootstrap.java:98)

SingleInstanceManager fails on Linux

I tested the single instance feature on a Linux machine but Linux will completely ignore the file locks and will start a new instance.

I know locks on UNIX are advisory, but java.nio.channels.FileChannel::tryLock should still return null; it doesn't. Adding random data to the lock file before locking (so it locks on an actual file region) also doesn't help.

Cannot build configuration if files don't already exist in installation location?

So maybe I am going about this all wrong, but here is how I'm starting:

I have a project for the updater/launcher that has dependencies on update4j and my application. I am writing a task that will build my Configuration file. So I am instantiating it with code and would eventually write out the file to be put...somewhere? - I assume that will go up to a URL at some point? I haven't gotten that far yet. Anyway - right now my task looks like this:

        Configuration config = Configurations.standard();
        config.update();
        config.launch();

Configurations.standard() looks like this:

        File install = new File("D:\\myapp");
        Configuration.Builder builder = Configuration
            .withBase(URI.create("https://museide.s3.amazonaws.com/updates/"), install.toPath())
            .property("app.name", "Chris's App")
            .property("user.location", "${user.home}/myapp/");

        File libs_folder = new File("D:\\Work\\MuseProject\\fxtest\\build\\install\\fxtest\\lib");
        for (File lib : libs_folder.listFiles())
            builder = builder.library(Library.Reference.at(lib.toPath()).uri(URI.create(lib.getName())).classpath().build());

        return builder.build();

It throws an exception on build() because the files do not yet exist in the install folder ("D:\myapp"):

Exception in thread "main" java.nio.file.NoSuchFileException: D:\myapp\animal-sniffer-annotations-1.14.jar
	at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:85)
	at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
	at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
	at java.base/sun.nio.fs.WindowsFileSystemProvider.isSameFile(WindowsFileSystemProvider.java:428)
	at java.base/java.nio.file.Files.isSameFile(Files.java:1522)
	at org.update4j.Configuration$Builder.build(Configuration.java:895)
	at mypackage.Configurations.standard(Configurations.java:25)
	at mypackage.Booter.main(Booter.java:14)

I had not intended to initially install any of my JARs - let the updater go and get them right away. No need to package something that will be immediately updated, right? That is what I've done with FXLauncher.

But this makes me wonder how it would work if I wanted to add new files.

It also seems like the install location is hardcoded in here. Like many apps, the user can choose alternate installation locations (global, this user, other hard drive, network drive, etc)...not sure how that is gonna work?

I feel like must be approaching the whole project wrong...but since you pitched this as a better FXLauncher, I came to this project expecting to do most of which I did with FXLauncher and do it in a vaguely similar way. But I'm 5 or 6 hours into this with nothing to show for it.

TIA!
Chris

Purpose of signer

Initially I thought the signer part was something that could be reused from jarsigner and what we have used for webstart to get signatures out of files. But that's not related at all is it?

How is the signer meant to be used? I guess generating a config dynamically, on the server, from a list of files (jars in this case) does not really make sense since part of the idea is to make sure the files are not tampered with. It would also mean that we would have to distribute our password.

Thanks

Search friendly name.

I would recommend you to change the project name from "uptodate" to something like "application-updater". as I think it is more likely to come up on search results.

jre8 compatible module

would it be possible to get a jre8 flavor of this? Not quite ready to update to jre9 yet.

I'd recommend using a multiple maven module approach.

-update4j-parent (root module)
-update4j-jre8 - the current src/main project, minus the jre9 stuff
-update4j - depends on the jre8 module and adds the jre9 stuff

Do not use printStackTrace

Using printStackTrace is not very useful, in target user environment. If we run the application without console, user has no chance to find out what is wrong and our application doesn't recieve exception, so it can't help the user either.

For example inside DefaultBootstrap.getRemoteConfig(), if the server is not running, FileNotFoundException is thrown, but not visible. The best way would be to throw specific exception like Update4jDownloadFailedException, but even just letting the FileNotFoundException would be fine, so we can display an actual error.

Calling UpdateHandler#failed(Throwable) would be actually another option.

Where to start

Hey , is there a full demo to see how every thing is working

Are there any tests?

Tests deliver a good impression of how specific parts of a software work. As you already mentioned, the documentation is a problematic point, the code including tests could be self explanatory.

Enable empty properties

It is possible in Java Webstart to define property with empty value and somehow it ends up in system properties as empty string. Sometimes, application can relly on such properties, but update4j doesn't allow them with IllegalArgumentException: Value must not be empty.

Could update4j convert null property value to empty string value? And accept this empty value?

No connect/read timeouts set on URLConnection

The part in Configuration where files are downloaded doesn't set any explicit timeouts on the URLConnection used. Depending on the JDK/OS the default timeouts may be infinite, causing the update method to block indefinitely.

It would be better to set finite timeouts by default so this doesn't occur.

Right way to hand over from JavaFX bootstrap to business app

In the example you have this javafx splash screen example where user launches delegate.jar and it downloads updates and launches business app.

I have build something similar; I have separate Updater app which also functions as installer with javafx gui using update4j-1.2.2.jar, running update logic in different thread and a CustomUpdateHandler updating javafx gui as it goes while showing user info on the currently downloading updates, then after update logic is finished, separate business app in run new jvm and updater does System.exit(0);

I'm wondering if this is an OK way to do it or should I implement it in an other way? Since the updater logic already does an javafx launch I can no longer use Configuration.launch after that am I rite?

Also I'm still a bit unclear on what needs to be done if the updater app itself requires updates, since the updaterapp jar will be locked while it is running.

Runtime run = Runtime.getRuntime(); Process process = run.exec("java -jar BUSINESSAPP.jar"); System.exit(0);

not everything that starts with "java." or "jdk." is a system module

ConfigImpl.java makes the assumption that everything that starts with "java." or "jdk." must be a system module but that is not the case.

Code in ConfigImpl.java:

if (reqName.startsWith("java.") || reqName.startsWith("jdk.")) {
...
}

For example the following maven dependency

com.sun.mail javax.mail 1.6.1

is an automatic Module and the jar file defines the following in it's META-INF/MANIFEST.MF

Automatic-Module-Name: java.mail

Delete old files automatically

I have a directory only containing files listed in the files section of the configuration and synced by update4j. I would like to see a feature, that when enabled, deletes files no longer in that configuration (with some kind of delta mechanism).

For example:
I start with this configuration and run update4j on it

<configuration>
    ...
    <files>
        <file path="/boot/updateHandler-V1.jar"/>
    </files>
</configuration>

then I upgrade my file to V2

<configuration>
    ...
    <files>
        <file path="/boot/updateHandler-V2.jar"/>
    </files>
</configuration>

After running update4j again my boot/ directory now contains two files updateHandler-V1.jar and updateHandler-V2.jar.
What I would like to have is that updateHandler-V1.jar gets deleted because it is no longer in the configuration, and only updateHandler-V2.jar remains in the directory.

Custom Authorization header when downloading files

Hello,

looking at the code, it looks like update4j expects all files to be downloaded to be publicly available.

Unfortunately, this is not an option for us. Our files need to be protected using OAuth 2.0 (or OpenID Connect). Because of this I am currently searching for a way to inject a custom "Authrorization" header for update4j to use when downloading any files.

I have written a little library that can be used (for example as a Service with a ServiceLoader) to retrieve the access token as a String. Update4j then only needs to dynamically load and call the service to retrieve the access token and can then use it as the Authorization header.

Using this method, update4j would be agnostic about the authentication method to use. What do you think?

Maybe it can even be abstracted further to allow the inclusion of any custom http headers, not just Authentication.

Support multiple release channels?

I've been using FXLauncher, but without Java9 support, I have to move on. I added support for this feature in FXLauncher pretty easily. Do you know of any reasons I could not make that work with update4j as well?

If you think it is feasible and have any thoughts on making it work, I'd love to hear them before I dive into the code.

Generate configuration and deploy from command line

So I found an example on how to create a configuration file here: https://github.com/update4j/update4j/wiki/Documentation#example-configuration-usage

But for a project with multiple files it will be annoying to add them one by one.
Is it possible to generate a configuration file and deploy from command line, e.g. something similar to this: https://github.com/edvin/fxldemo ?

It would be great to be able to generate a config file of the whole output directory of my app. Possibly with some kind of filter that excludes files from the config (similar to a .gitignore).
org.apache.commons.io.filefilter.WildcardFileFilter can be used to handle filter entries.

I will be happy to help developing this feature, but want to hear your opinion first. Is this even something you want to add to this project?

Business arguments from the configuration file

Current situation:
If I understand it correctly, now, business arguments must be provided statically on the command line.

We have different configuration files with different main classes and different arguments. Main class is now defined in config as a property default.launcher.main.class.

Improvement:
It would be nice if business arguments could be passed from a similar property like default.launcher.arguments.

Handing over from bootstrap to business application

What I've done now is calling the Bootstrap.main() method in my original main method and handing the application in to a Delegate service provider. I suppose now (I'm not too sure) I'll have to get the config.xml from a remote url and call launch method to launch the 'launcher' in tag to hand the application back to my business application.

But how do I implement the launcher and put it in the config file in the first place? I found a method launcher() under Configuration.Builder but it takes a parameter of type Class<? extends Launcher> which seems weird as a class can't extends an interface. Passing an implementation of Launcher is a no-go as well. What am I doing wrong here?

Also, can you guide me on how to integrate UpdateHandler and SingleInstanceManager? I suppose I'll have to implement an UpdateHandler service provider just like what I've done to Delegate and write some logic there, but I have no idea about where and when it should be called. For the SingleInstanceManager, I have totally no clue as to how to use that.

What does withBase() actually specify?

The first parameter (URI) is where the files will be downloaded from?
The second is where the files will go during the update? If I use the version without the Path parameter, I get this exception:

java.lang.IllegalArgumentException: 'other' is different type of Path
at java.base/sun.nio.fs.WindowsPath.relativize(WindowsPath.java:400)
at java.base/sun.nio.fs.WindowsPath.relativize(WindowsPath.java:42)
at org.update4j.service.DefaultUpdateHandler.doneCheckUpdateLibrary(DefaultUpdateHandler.java:45)
at org.update4j.Configuration.updateImpl(Configuration.java:274)
at org.update4j.Configuration.update(Configuration.java:195)
at org.update4j.Configuration.update(Configuration.java:183)
at org.update4j.Configuration.update(Configuration.java:175)
at mypackage.Booter.main(Booter.java:15)

As a suggestion - maybe some more descriptive parameter names would help more than javadoc. A parameter of type Path with name "path" is redundant. If the name was "install_location", that would say a lot. Same with the URI parameter named "uri".

I don't suppose you have a working example? That would answer a lot of questions :)

Support older versions of Java

Now that #9 was resolved and we can now add jars to the classpath, (originally we only supported adding to modulepath) we are looking into supporting Java 8 or even earlier via deploying a multi-release jar.

The same configuration file will still work, but it will ignore the modulepath attribute.

Let us hear your thoughts.

Absolute path required when using parameterized base path?

When I used this to start start the declaration of my Configuration:

        Configuration.Builder builder = Configuration
            .withBase(URI.create(extension.getUri()), Paths.get("D:\\myapp\\"))

Then the Configuration.Builder.build() call succeeds. But when I use a variable as shown in the example configuration.xml:

        Configuration.Builder builder = Configuration
            .withBase(URI.create(extension.getUri()), Paths.get("${user.dir}"))

I get an exception in Configuration.Builder.build():

Caused by: java.lang.IllegalArgumentException: Absolute path required: ${user.dir}\lib\javax.activation-1.2.0.jar

The library is added as shown in the section of docs that said "ideal for bootstrap dependencies":

        for (File lib : libs)
            builder = builder.library(Library.Reference
                .at(lib.getAbsolutePath())
                .path(Paths.get(String.format("lib/%s", lib.getName())))
                .build());

Are these two features somehow incompatible with each other?

TIA!
Chris

Should throw exception when missing config.xml

The getLocalConfig(boolean ignoreFileNotFound) method in DefaultBootstrap should rethrow execption if local config not found, not only print stack trace.
Otherwise the launcher is unable to know the config.xml is missing.

Extracting lib as jar

Finally got everything working in my local enviroment but when i tried to run as external jar.. just issues!
Maybe you found or seen before. After some stackoverflow + more couldnt find the answer.

Called with: java -jar --add-modules java.xml.bind exampleLauncher.jar

And even i created inside jar:
exampleLauncher.jar\META-INF\services\org.update4j.service.UpdateHandler

And inside the file: org.update4j.service.UpdateHandler # Standard

But the error still appearing, i had to create this above to java could find UpdateHandler class, the worst part its that is running inside my ide!

Exception in thread "AWT-EventQueue-0" java.util.ServiceConfigurationError: org.
update4j.service.UpdateHandler: org.update4j.service.UpdateHandler Unable to get
public no-arg constructor

at java.base/java.util.ServiceLoader.fail(Unknown Source)
at java.base/java.util.ServiceLoader.getConstructor(Unknown Source)
at java.base/java.util.ServiceLoader.access$1000(Unknown Source)
at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext
Service(Unknown Source)
at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext
(Unknown Source)
at java.base/java.util.ServiceLoader$2.hasNext(Unknown Source)
at java.base/java.util.ServiceLoader$ProviderSpliterator.tryAdvance(Unkn
own Source)
at java.base/java.util.Spliterator.forEachRemaining(Unknown Source)
at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown S
ource)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(Unkn
own Source)
at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.base/java.util.stream.ReferencePipeline.collect(Unknown Source)
at org.update4j.service.Service.loadService(Service.java:41)
at org.update4j.service.Service.loadService(Service.java:76)
at org.update4j.Configuration.updateImpl(Configuration.java:281)
at org.update4j.Configuration.update(Configuration.java:259)
at org.update4j.Configuration.update(Configuration.java:255)
at org.update4j.Configuration.update(Configuration.java:247)
at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown S
ource)
at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(Unkno
wn Source)
at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(Unkno
wn Source)
at java.desktop/javax.swing.DefaultButtonModel.setPressed(Unknown Source
)
at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased
(Unknown Source)
at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)

    at java.desktop/java.awt.Component.processEvent(Unknown Source)
    at java.desktop/java.awt.Container.processEvent(Unknown Source)
    at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
    at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknow

n Source)
at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown
Source)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Sou
rce)
at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.EventQueue.access$600(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doInt
ersectionPrivilege(Unknown Source)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doInt
ersectionPrivilege(Unknown Source)
at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doInt
ersectionPrivilege(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unkn
own Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown
Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unkn
own Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.NoSuchMethodException: org.update4j.service.UpdateHandler.<
init>()

at java.base/java.lang.Class.getConstructor0(Unknown Source)
at java.base/java.lang.Class.getConstructor(Unknown Source)
at java.base/java.util.ServiceLoader$1.run(Unknown Source)
at java.base/java.util.ServiceLoader$1.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Native Method)

Stop on unknown command line argument

Issue

If you pass in the cert option, without equal sign but a space (should be true for the other options too) --cert ./app.crt instead of --cert=./app.crt the certificate is ignored silently. It took me quite a while to find out what's wrong (why didn't it fail with corrupt signatures). As --cert is parsed as own argument, it doesn't match the if clause in DefaultBootstrap and falls through to the end.

As the arguments are passed further to the main class to execute, not all arguments are known.

Solution

May be a further argument (i.e. --mainArgs) should be used to collect all the args, that are passed to the main class (i.e. --mainArgs="--arg1=3 --arg2 foo --arg3 bar"?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.