@@ -15,6 +15,7 @@
package de.ks.preload;

import de.ks.launch.Launcher;
import de.ks.standbein.GuiceSupport;
import javafx.application.Application;
import javafx.stage.Stage;
import org.slf4j.Logger;
@@ -26,12 +27,14 @@ public abstract class PreloaderApplication extends Application {

@Override
public void start(Stage primaryStage) throws Exception {
Launcher launcher = GuiceSupport.get(Launcher.class);

stage = primaryStage;
try {
startPreloader(stage);
Launcher.instance.setPreloaderInstance(this);
launcher.setPreloaderInstance(this);
} catch (Exception e) {
Launcher.instance.setPreloaderInstance(null);
launcher.setPreloaderInstance(null);
log.error("Could not start preloader {}", getClass().getName(), e);
throw e;
}
@@ -0,0 +1,136 @@
/**
* Copyright [2015] [Christian Loehnert]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ks.version;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.*;
import java.net.URL;
import java.util.Optional;
import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

public class DefaultVersionProvider implements VersionProvider {
public static final String APP_VERSION_KEY = "app.version";

private static final Logger log = LoggerFactory.getLogger(DefaultVersionProvider.class);
private final URL manifestUrl;
private File versionFile;

@Inject
public DefaultVersionProvider(@ManifestMfUrl URL manifestUrl, @VersionFile File versionFile) {
this.manifestUrl = manifestUrl;
this.versionFile = versionFile;
}

@Override
public Optional<Integer> getLastVersion() {
try (FileInputStream stream = new FileInputStream(versionFile)) {
Properties properties = new Properties();
properties.load(stream);
String value = properties.getProperty(APP_VERSION_KEY, "-1");
return Optional.of(Integer.valueOf(value));
} catch (IOException e) {
log.warn("No {} present, will assume version -1", versionFile);
return Optional.empty();
}
}

@Override
public void writeLastVersion(int version) {
if (!versionFile.exists()) {
try {
versionFile.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

try (FileOutputStream fileOutputStream = new FileOutputStream(versionFile)) {
Properties properties = new Properties();
properties.setProperty(APP_VERSION_KEY, String.valueOf(version));
properties.store(fileOutputStream, "42");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public int getCurrentVersion() {
String version = getManifestInfo("Implementation-Version");
if (version == null) {
return 0;
} else {
int indexOf = version.lastIndexOf("-");
if (indexOf > 0) {
version = version.substring(0, indexOf);
}
version = version.replaceAll("\\.", "");
return Integer.valueOf(version);
}
}

@Override
public String getVersionString() {
String version = getManifestInfo("Implementation-Version");
return version;
}

public String getManifestInfo(String key) {
try {
InputStream is = manifestUrl.openStream();
if (is != null) {
Manifest manifest = new Manifest(is);
Attributes mainAttribs = manifest.getMainAttributes();
String value = mainAttribs.getValue(key);
log.debug("From {} {}: {}", manifestUrl, key, value);
return value;
} else {
log.warn("No manifest.mf in {}", manifestUrl);
}
} catch (Exception e) {
log.error("Could not open manifest.mf from {}", manifestUrl, e);
}
return null;
}

//
// private URL discoverManifestUrl() {
// URL ownerLocation = owner.getProtectionDomain().getCodeSource().getLocation();
// log.info("Using owner location: '{}'", ownerLocation.getFile());
//
// ArrayList<URL> urlCandidates = new ArrayList<>();
// Enumeration resEnum;
// try {
// resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
// while (resEnum.hasMoreElements()) {
// URL url = (URL) resEnum.nextElement();
// urlCandidates.add(url);
// if (url.getFile().contains(ownerLocation.getFile())) {
// log.info("Found URL to read manifest.mf {}", url);
// return url;
// }
// }
// } catch (IOException e1) {
// log.error("Unknown exception ", e1);
// }
// log.info("Found no manifest.mf url. Candidates: {}", urlCandidates.stream().map(url -> "\n\t" + url.getFile()).collect(Collectors.toList()));
// return null;
// }
}
@@ -1,37 +1,29 @@
/*
* Copyright [2014] [Christian Loehnert, krampenschiesser@gmail.com]
/**
* Copyright [2015] [Christian Loehnert]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ks.version;

package de.ks.menu.event;

import de.ks.menu.MenuItemDescriptor;

/**
*
*/
public class MenuItemClickedEvent {
protected final MenuItemDescriptor item;

public MenuItemClickedEvent(MenuItemDescriptor item) {
this.item = item;
}
import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

public Class<?> getTarget() {
return item.getTarget();
}
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

public MenuItemDescriptor getItem() {
return item;
}
@Qualifier
@Target({FIELD, PARAMETER, METHOD})
@Retention(RUNTIME)
public @interface ManifestMfUrl {
}
@@ -0,0 +1,29 @@
/**
* Copyright [2015] [Christian Loehnert]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ks.version;

import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Qualifier
@Target({FIELD, PARAMETER, METHOD})
@Retention(RUNTIME)
public @interface VersionFile {
}

This file was deleted.

@@ -0,0 +1,34 @@
/**
* Copyright [2015] [Christian Loehnert]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ks.version;

import com.google.inject.AbstractModule;

import java.io.File;
import java.net.URL;

public abstract class VersionModule extends AbstractModule {
@Override
protected void configure() {
bind(VersionProvider.class).to(DefaultVersionProvider.class);
bind(URL.class).annotatedWith(ManifestMfUrl.class).toInstance(getManifestUrl());
bind(File.class).annotatedWith(VersionFile.class).toInstance(getVersionFile());
}

protected abstract File getVersionFile();

protected abstract URL getManifestUrl();
}
@@ -1,26 +1,31 @@
/*
* Copyright [2014] [Christian Loehnert, krampenschiesser@gmail.com]
/**
* Copyright [2015] [Christian Loehnert]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ks.version;

import com.google.inject.ImplementedBy;

import java.util.Optional;

@ImplementedBy(DefaultVersionProvider.class)
public interface VersionProvider {
Optional<Integer> getLastVersion();

package de.ks.activity.context;
void writeLastVersion(int version);

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.CDI;
int getCurrentVersion();

public class ActivityContextProducer {
@Produces
public ActivityContext getContext() {
return (ActivityContext) CDI.current().getBeanManager().getContext(ActivityScoped.class);
}
String getVersionString();
}
@@ -14,114 +14,54 @@
*/
package de.ks.version;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import de.ks.SubclassInstantiator;
import de.ks.launch.Launcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import java.util.*;

public class Versioning {
private static final Logger log = LoggerFactory.getLogger(Versioning.class);
public static final String VERSION_PACKAGES = "version.packages";
public static final String VERSION_PROPERTIES_FILENAME = "version.properties";
public static final String PACKAGE_SEPARATOR = Launcher.PACKAGE_SEPARATOR;
public static final String APP_VERSION = "app.version";

private final ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newCachedThreadPool(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("versioning-%d").build());
private final File versionFile;
private final Class<?> owner;
private final VersionInfo versionInfo;
private final SubclassInstantiator instantiator;
private final Collection<InitialImport> initialImports;
private final Collection<VersionUpgrade> upgrades;
private final VersionProvider versionProvider;

public Versioning(File versionFile, Class<?> owner) {
this.versionFile = versionFile;
this.owner = owner;
versionInfo = new VersionInfo(owner);
executorService.setKeepAliveTime(1, TimeUnit.SECONDS);
instantiator = new SubclassInstantiator(executorService, getClass().getPackage(), VERSION_PROPERTIES_FILENAME, VERSION_PACKAGES, PACKAGE_SEPARATOR);
@Inject
public Versioning(Collection<InitialImport> initialImports, Collection<VersionUpgrade> upgrades, VersionProvider versionProvider) {
this.initialImports = initialImports;
this.upgrades = upgrades;
this.versionProvider = versionProvider;
}

public boolean isInitialImport() {
return getLastVersion() == -1;
return !getLastVersion().isPresent();
}

public int getCurrentVersion() {
return versionInfo.getVersion();
return versionProvider.getCurrentVersion();
}

public VersionInfo getVersionInfo() {
return versionInfo;
}

public int getLastVersion() {
try (FileInputStream stream = new FileInputStream(versionFile)) {
Properties properties = new Properties();
properties.load(stream);
String value = properties.getProperty(APP_VERSION, "-1");
return Integer.valueOf(value);
} catch (IOException e) {
log.warn("No {} present, will assume version -1", versionFile);
return -1;
}
}

public void writeLastVersion(int version) {
if (!versionFile.exists()) {
try {
versionFile.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

try (FileOutputStream fileOutputStream = new FileOutputStream(versionFile)) {
Properties properties = new Properties();
properties.setProperty(APP_VERSION, String.valueOf(version));
properties.store(fileOutputStream, "42");
} catch (IOException e) {
throw new RuntimeException(e);
}
public Optional<Integer> getLastVersion() {
return versionProvider.getLastVersion();
}

public void upgradeToCurrentVersion() {
if (isInitialImport()) {
getInitialImportRunners().forEach(r -> r.performInitialImport());
writeLastVersion(getCurrentVersion());
} else if (getLastVersion() < getCurrentVersion()) {
List<VersionUpgrade> upgraders = getVersionUpgrades();
initialImports.forEach(InitialImport::performInitialImport);
versionProvider.writeLastVersion(getCurrentVersion());
} else if (getLastVersion().get() < getCurrentVersion()) {
List<VersionUpgrade> upgraders = new ArrayList<>(upgrades);
Collections.sort(upgraders);

upgraders.stream().filter(upgrader -> upgrader.getVersion() > getLastVersion()).forEach(upgrader -> {
upgraders.stream().filter(upgrader -> upgrader.getVersion() > getLastVersion().get()).forEach(upgrader -> {
log.info("Performing upgrade from version {} to {} using {}", getLastVersion(), upgrader.getVersion(), upgrader.getClass().getSimpleName());
upgrader.performUpgrade();
});
writeLastVersion(getCurrentVersion());
versionProvider.writeLastVersion(getCurrentVersion());
} else {
log.debug("no upgrade nessessary");
}
}

protected List<InitialImport> getInitialImportRunners() {
List<InitialImport> importers = instantiator.instantiateSubclasses(InitialImport.class);
log.debug("Found {} initial importers: {}", importers.size(), importers);
return importers;
}

protected List<VersionUpgrade> getVersionUpgrades() {
List<VersionUpgrade> upgraders = instantiator.instantiateSubclasses(VersionUpgrade.class);
log.debug("Found {} initial importers: {}", upgraders.size(), upgraders);
return upgraders;

}
}
@@ -0,0 +1,43 @@
/**
* Copyright [2015] [Christian Loehnert]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.ks;

import com.dummy.other.ServiceB;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.multibindings.Multibinder;
import de.ks.launch.Service;
import de.ks.launch.ServiceA;

import javax.inject.Singleton;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class LauncherModule extends AbstractModule {
@Override
protected void configure() {
Multibinder<Service> serviceBinder = Multibinder.newSetBinder(binder(), Service.class);
serviceBinder.addBinding().to(ServiceA.class).in(Singleton.class);
serviceBinder.addBinding().to(ServiceB.class).in(Singleton.class);
}

@Provides
@Singleton
public ExecutorService getExecutor() {
return Executors.newCachedThreadPool(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("launcher-%d").build());
}
}
@@ -14,13 +14,14 @@
*/
package de.ks.launch;

import com.dummy.other.ServiceB;
import com.google.inject.Guice;
import com.google.inject.Injector;
import de.ks.LauncherModule;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ForkJoinPool;

@@ -32,15 +33,11 @@ public class LauncherTest {

@Before
public void setUp() throws Exception {
launcher = new Launcher(false);
}

@Test
public void testDiscoverServices() throws Exception {
List<Service> services = launcher.discoverServices();
assertEquals(2, services.size());
assertTrue(ServiceB.class.isInstance(services.get(1)));
assertTrue(ServiceA.class.isInstance(services.get(0)));
Injector injector = Guice.createInjector(new LauncherModule());
Launcher instance = injector.getInstance(Launcher.class);
Launcher instance2 = injector.getInstance(Launcher.class);
assertSame(instance, instance2);
launcher = instance;
}

@Test
@@ -0,0 +1,87 @@
package de.ks.version;

import com.google.common.base.StandardSystemProperty;
import org.junit.Before;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static org.junit.Assert.*;

public class DefaultVersionProviderTest {

private DefaultVersionProvider provider;
private Path manifestPath;
private Path versionPath;

@Before
public void setUp() throws Exception {
String tempDir = StandardSystemProperty.JAVA_IO_TMPDIR.value();
manifestPath = Paths.get(tempDir, "MyManifest.mf");
deleteIfExists(manifestPath);
versionPath = Paths.get(tempDir, "MyVersion.txt");
deleteIfExists(versionPath);

Files.write(manifestPath, Arrays.asList(//
"Manifest-Version: 1.0",//
"Implementation-Title: testing",//
"Implementation-Version: 0.2.3-SNAPSHOT",//
"Implementation-Vendor: krampenschiesser"//
));

URL manifestMfUrl = manifestPath.toUri().toURL();
File versionFile = versionPath.toFile();

provider = new DefaultVersionProvider(manifestMfUrl, versionFile);
}

private void deleteIfExists(Path path) throws IOException {
if (Files.exists(path)) {
Files.delete(path);
}
}

@Test
public void testWriteVersion() throws Exception {
provider.writeLastVersion(420);
assertTrue(Files.exists(versionPath));
List<String> lines = Files.readAllLines(versionPath);
assertEquals(3, lines.size());
assertEquals(1, lines.stream().filter(l -> !l.startsWith("#")).count());
assertEquals(DefaultVersionProvider.APP_VERSION_KEY + "=420", lines.get(2));
}

@Test
public void testNoLastVersion() throws Exception {
Optional<Integer> lastVersion = provider.getLastVersion();
assertFalse(lastVersion.isPresent());
}

@Test
public void testReadLastVersion() throws Exception {
Files.write(versionPath, Collections.singleton(DefaultVersionProvider.APP_VERSION_KEY + " = 142"));
int lastVersion = provider.getLastVersion().get();
assertEquals(142, lastVersion);
}

@Test
public void testReadFromMetaInf() throws Exception {
int currentVersion = provider.getCurrentVersion();
assertEquals(23, currentVersion);
}

@Test
public void testGetVersionString() throws Exception {
String versionString = provider.getVersionString();
assertEquals("0.2.3-SNAPSHOT", versionString);
}
}
@@ -15,10 +15,14 @@
package de.ks.version;

import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

import static org.junit.Assert.assertEquals;
@@ -27,47 +31,18 @@ public class VersionTest {
private static final Logger log = LoggerFactory.getLogger(VersionTest.class);
public static AtomicLong upgradeCounter = new AtomicLong();

@Test
public void testReadFromMetaInf() throws Exception {
VersionInfo versionInfo = new VersionInfo(getClass());
log.info(versionInfo.getDescription());
int versionNumber = versionInfo.getVersion();
assertEquals(23, versionNumber);
}

@Test
public void testWriteVersion() throws Exception {
File testversion = getVersionFile();
Versioning versioning = new Versioning(testversion, VersionTest.class);
assertEquals(-1, versioning.getLastVersion());
versioning.writeLastVersion(230);
assertEquals(230, versioning.getLastVersion());
}

protected File getVersionFile() {
File tempDir = new File(System.getProperty("java.io.tmpdir"));
File testversion = new File(tempDir, "testversion");
if (testversion.exists()) {
testversion.delete();
}
return testversion;
}

@Test
public void testUpgrade() throws Exception {
upgradeCounter.set(0);
Versioning versioning = new Versioning(getVersionFile(), VersionTest.class) {
@Override
public int getLastVersion() {
return 1;
}

@Override
public int getCurrentVersion() {
return 3;
}
};
VersionProvider versionProvider = Mockito.mock(VersionProvider.class);
Mockito.when(versionProvider.getLastVersion()).thenReturn(Optional.of(1));
Mockito.when(versionProvider.getCurrentVersion()).thenReturn(3);

Collection<InitialImport> imports = Collections.emptyList();
Collection<VersionUpgrade> upgrades = Arrays.asList(new UpgraderVersion1(), new UpgraderVersion2(), new UpgraderVersion3());

Versioning versioning = new Versioning(imports, upgrades, versionProvider);
versioning.upgradeToCurrentVersion();
assertEquals(2, upgradeCounter.get());
}

This file was deleted.