Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Several enhancements #36

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions boot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,7 @@
<name>Apache Karaf Minho :: Boot</name>

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- are you sure, probably not -->
</dependencies>

<build>
Expand Down
61 changes: 37 additions & 24 deletions boot/src/main/java/org/apache/karaf/minho/boot/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,57 @@
*/
package org.apache.karaf.minho.boot;

import lombok.extern.java.Log;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.function.Predicate;
import java.util.logging.Logger;

import static java.util.Optional.ofNullable;

@Log
public class Main {
private Main() {
// no-op
}

public static final void main(String[] args) throws Exception {
boolean minhoJar = false;
minhoJar = (System.getenv("MINHO_JAR") != null) ? System.getenv("MINHO_JAR").equalsIgnoreCase("true") : minhoJar;
minhoJar = (System.getProperty("minho.jar") != null) ? System.getProperty("minho.jar").equalsIgnoreCase("true") : minhoJar;
public static void main(final String... args) throws Exception {
final boolean minhoJar = Boolean.parseBoolean(System.getenv("MINHO_JAR")) || Boolean.parseBoolean(System.getProperty("minho.jar"));

final var log = Logger.getLogger(Main.class.getName());
if (!minhoJar) {
log.info("Starting runtime in exploded mode");
// try to load classpath
String minhoLib = (System.getProperty("minho.lib") != null) ? System.getProperty("minho.lib") : System.getProperty("user.dir");
final var minhoLib = ofNullable(System.getProperty("minho.lib"))
.orElseGet(() -> System.getProperty("user.dir"));
System.out.println("Minho lib: " + minhoLib);
Path libFolder = Paths.get(minhoLib);
ArrayList<URL> urls = new ArrayList<URL>();
Files.walkFileTree(libFolder, new SimpleFileVisitor<>() {
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {
if (!Files.isDirectory(file)) {
urls.add(file.toFile().toURI().toURL());
final var libFolder = Paths.get(minhoLib);
try (final var walk = Files.walk(libFolder)) {
final var urls = walk
.filter(Predicate.not(Files::isDirectory))
.map(it -> {
try {
return it.toUri().toURL();
} catch (final MalformedURLException e) {
throw new IllegalStateException(e);
}
})
.toArray(URL[]::new);
final var classLoader = new URLClassLoader(urls);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
classLoader.close();
} catch (final IOException e) {
// no-op, not critical
}
return FileVisitResult.CONTINUE;
}
});
URLClassLoader classLoader = new URLClassLoader(urls.toArray(new URL[]{}));
Thread.currentThread().setContextClassLoader(classLoader);
}, Main.class.getName() + "-classloader-close"));
Thread.currentThread().setContextClassLoader(classLoader);
}
} else {
log.info("Starting runtime in uber jar mode");
}
Minho.builder().build().start();
SimpleMain.main(args);
}

}
83 changes: 48 additions & 35 deletions boot/src/main/java/org/apache/karaf/minho/boot/Minho.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,38 @@
*/
package org.apache.karaf.minho.boot;

import lombok.Builder;
import lombok.Data;
import lombok.extern.java.Log;
import org.apache.karaf.minho.boot.service.ServiceRegistry;
import org.apache.karaf.minho.boot.spi.Service;
import org.apache.karaf.minho.boot.spi.ServiceLoader;
import org.apache.karaf.minho.boot.spi.impl.DefaultLoaderService;

import java.util.Comparator;
import java.util.stream.Stream;
import java.util.Objects;

/**
* Main Karaf runtime.
*/
@Log
@Builder
@Data
public class Minho implements AutoCloseable {

private static Minho instance;

public class Minho implements AutoCloseable, Service {
private final ServiceLoader loader;
private final ServiceRegistry serviceRegistry = new ServiceRegistry();
private volatile boolean closed = false;

protected Minho(final ServiceLoader loader) {
this.loader = loader;
}

public ServiceRegistry getServiceRegistry() {
return serviceRegistry;
}

@Override
public String name() {
return "minho";
}

@Override
public int priority() {
return Integer.MIN_VALUE;
}

/**
* Start the Karaf runtime.
Expand All @@ -47,40 +57,43 @@ public class Minho implements AutoCloseable {
*/
public Minho start() {
// log format
if (System.getProperty("java.util.logging.config.file") == null) {
if (System.getenv("KARAF_LOG_FORMAT") != null) {
System.setProperty("java.util.logging.SimpleFormatter.format", System.getenv("KARAF_LOG_FORMAT"));
}
if (System.getProperty("java.util.logging.SimpleFormatter.format") == null) {
System.setProperty("java.util.logging.SimpleFormatter.format", "%1$tF %1$tT.%1$tN %4$s [ %2$s ] : %5$s%6$s%n");
}
if (System.getProperty("java.util.logging.config.file") == null &&
System.getProperty("java.util.logging.SimpleFormatter.format") == null) {
System.setProperty("java.util.logging.SimpleFormatter.format",
Objects.requireNonNullElse(System.getenv("MINHO_LOG_FORMAT"), "%1$tF %1$tT.%1$tN %4$s [ %2$s ] : %5$s%6$s%n"));
}
(this.loader == null ? loadServices() : this.loader.load()).forEach(serviceRegistry::add);
serviceRegistry.add(this);
loader.load().forEach(serviceRegistry::add);
serviceRegistry.start();
instance = this;
return instance;
}

private Stream<Service> loadServices() {
return java.util.ServiceLoader.load(Service.class).stream().map(java.util.ServiceLoader.Provider::get)
.sorted(Comparator.comparingInt(service -> Integer.getInteger(service.name() + ".priority", service.priority())));
return this;
}

/**
* Close (stop) the Karaf runtime.
* Close (stop) the Minho runtime.
*/
@Override
public void close() {
if (closed) {
return;
}
closed = true;
serviceRegistry.close();
}

/**
* Retrieve the Minho instance.
*
* @return the Minho instance.
*/
public static Minho getInstance() {
return instance;
public static Builder builder() {
return new Builder();
}

public static class Builder {
private ServiceLoader loader = new DefaultLoaderService();

public Builder loader(final ServiceLoader loader) {
this.loader = loader;
return this;
}

public Minho build() {
return new Minho(loader);
}
}
}
51 changes: 51 additions & 0 deletions boot/src/main/java/org/apache/karaf/minho/boot/SimpleMain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.karaf.minho.boot;

import org.apache.karaf.minho.boot.service.ConfigService;

import java.util.concurrent.CountDownLatch;

public class SimpleMain {
private SimpleMain() {
// no-op
}

public static void main(final String... args) throws Exception {
try (final var instance = Minho.builder().build().start()) {
final var registry = instance.getServiceRegistry();
final var awaiter = registry.get(Awaiter.class);
if (awaiter != null) {
awaiter.await();
} else if (Boolean.getBoolean(registry.get(ConfigService.class).property("minho.awaiter.implicit", "false"))) {
new CountDownLatch(1).await();
}
}
}

/**
* Service implementation enabling the main to not quit immediately if set.
* Often used for servers.
*/
public interface Awaiter {
/**
* Wait until the application completes and can exit.
*/
void await();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,71 @@
*/
package org.apache.karaf.minho.boot.config;

import lombok.Data;

import java.util.HashMap;
import java.util.Map;

@Data
// todo: move to record
public class Application {

private String name;
private String version;
private String url;
private String type;
private String profile;
private Map<String, String> properties = new HashMap<>();

public String getProperty(String key) {
return getProperty(key, null);
public String property(final String key) {
return property(key, null);
}

public String property(String key, String defaultValue) {
return Config.property(key, properties, defaultValue);
}

public String getProperty(String key, String defaultValue) {
return Config.getProperty(key, properties, defaultValue);
public String getName() {
return name;
}

public void setName(final String name) {
this.name = name;
}

public String getVersion() {
return version;
}

public void setVersion(final String version) {
this.version = version;
}

public String getUrl() {
return url;
}

public void setUrl(final String url) {
this.url = url;
}

public String getType() {
return type;
}

public void setType(final String type) {
this.type = type;
}

public String getProfile() {
return profile;
}

public void setProfile(final String profile) {
this.profile = profile;
}

public Map<String, String> getProperties() {
return properties;
}

public void setProperties(final Map<String, String> properties) {
this.properties = properties;
}
}
Loading