Skip to content
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
14 changes: 14 additions & 0 deletions deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devservices-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.quarkus.jsch.deployment;

import io.quarkus.runtime.annotations.ConfigDocSection;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;

@ConfigMapping(prefix = "quarkus.jsch")
@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
public interface JSchBuildTimeConfig {

/**
* The JSch DevService configuration.
*/
@ConfigDocSection
JSchDevServiceConfig devservices();

@ConfigGroup
public interface JSchDevServiceConfig {

/**
* Enable the JSch DevService.
*/
@WithDefault("true")
boolean enabled();

/**
* The image to use for the JSch DevService.
*/
@WithDefault("linuxserver/openssh-server")
String image();

/**
* The port to use for the JSch DevService.
*/
@WithDefault("2222")
int port();

/**
* The username to use for the JSch DevService.
*/
@WithDefault("quarkus")
String username();

/**
* The password to use for the JSch DevService.
*/
@WithDefault("quarkus")
String password();

/**
* Whether to reuse the container for the JSch DevService.
*/
@WithDefault("false")
boolean reuse();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.jsch.deployment;

import java.util.Map;

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;

import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildSteps;
import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;

@BuildSteps(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
public class JSchDevServiceProcessor {

@BuildStep
DevServicesResultBuildItem startDevServices(JSchBuildTimeConfig config) {
if (!config.devservices().enabled()) {
return null;
}

DockerImageName dockerImageName = DockerImageName.parse(config.devservices().image());
GenericContainer container = new GenericContainer<>(dockerImageName)
.withEnv("USER_NAME", config.devservices().username())
.withEnv("USER_PASSWORD", config.devservices().password())
.withEnv("PASSWORD_ACCESS", "true")
.withExposedPorts(config.devservices().port())
.withReuse(config.devservices().reuse());

container.start();

Map<String, String> configOverrides = Map.of(
"quarkus.jsch.session.host", container.getHost(),
"quarkus.jsch.session.port", container.getMappedPort(config.devservices().port()).toString(),
"quarkus.jsch.session.username", config.devservices().username(),
"quarkus.jsch.session.password", config.devservices().password(),
"quarkus.jsch.session.config.StrictHostKeyChecking", "no");

return new DevServicesResultBuildItem.RunningDevService(JSchProcessor.FEATURE, container.getContainerId(),
container::close, configOverrides)
.toBuildItem();
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
package io.quarkus.jsch.deployment;

import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;

import jakarta.enterprise.context.RequestScoped;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;

import com.jcraft.jsch.Session;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
import io.quarkus.jsch.JSchSession;
import io.quarkus.jsch.JSchSessions;
import io.quarkus.jsch.runtime.JSchSessionRecorder;
import io.quarkus.jsch.runtime.PortWatcherRunTime;

class JSchProcessor {

private static final String FEATURE = "jsch";
public static final String FEATURE = "jsch";

@BuildStep
FeatureBuildItem feature() {
Expand Down Expand Up @@ -131,4 +154,65 @@ ReflectiveClassBuildItem reflection() {
"com.jcraft.jsch.UserAuthPublicKey")
.fields().methods().build();
}

@BuildStep
void registerAdditionalBeans(BuildProducer<AdditionalBeanBuildItem> additionalBeans) {
additionalBeans.produce(AdditionalBeanBuildItem.builder()
.addBeanClass(JSchSessions.class)
.setUnremovable()
.setDefaultScope(DotName.createSimple(RequestScoped.class))
.build());

additionalBeans.produce(AdditionalBeanBuildItem.builder()
.addBeanClass(JSchSession.class)
.build());
}

@BuildStep
void produceSessions(
BuildProducer<JSchSessionBuildItem> jschSessionBuildItemBuildProducer,
BeanArchiveIndexBuildItem indexBuildItem) {
IndexView index = indexBuildItem.getIndex();
Collection<AnnotationInstance> jschSessionAnnotations = index.getAnnotations(JSchSession.class);

if (jschSessionAnnotations.isEmpty()) {
// No @JschSession annotations found
return;
}

for (AnnotationInstance annotation : jschSessionAnnotations) {
AnnotationValue value = annotation.value();
String name = value != null ? value.asString() : JSchSession.DEFAULT_SESSION_NAME;
jschSessionBuildItemBuildProducer.produce(new JSchSessionBuildItem(name));
}
}

@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
void sessionRecorder(JSchSessionRecorder recorder,
List<JSchSessionBuildItem> sessionBuildItems,
ShutdownContextBuildItem shutdown,
BuildProducer<SyntheticBeanBuildItem> syntheticBeans) {
if (sessionBuildItems.isEmpty()) {
return;
}

for (JSchSessionBuildItem sessionBuildItem : sessionBuildItems) {
// create session
Supplier<Session> sessionSupplier = recorder.jschSessionSupplier(sessionBuildItem.name());
SyntheticBeanBuildItem.ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem
.configure(Session.class)
.scope(RequestScoped.class)
.setRuntimeInit()
.unremovable()
.supplier(sessionSupplier);

configurator.addQualifier()
.annotation(JSchSession.class)
.addValue("value", sessionBuildItem.name())
.done();

syntheticBeans.produce(configurator.done());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.quarkus.jsch.deployment;

import io.quarkus.builder.item.MultiBuildItem;

/**
* A build item that registers a JSch session created by annotation.
*/
public final class JSchSessionBuildItem extends MultiBuildItem {

private final String name;

public JSchSessionBuildItem(String name) {
this.name = name;
}

public String name() {
return name;
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}

if (!(obj instanceof JSchSessionBuildItem)) {
return false;
}

JSchSessionBuildItem other = (JSchSessionBuildItem) obj;
return name.equals(other.name);
}

@Override
public int hashCode() {
return name.hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import io.quarkus.test.QuarkusDevModeTest;

public class JschDevModeTest {
public class JSchDevModeTest {

// Start hot reload (DevMode) test with your extension loaded
@RegisterExtension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import io.quarkus.test.QuarkusUnitTest;

public class JschTest {
public class JSchTest {

// Start unit test with your extension loaded
@RegisterExtension
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/includes/attributes.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
:quarkus-version: 3.14.4
:quarkus-version: 3.15.1
:quarkus-jsch-version: 3.0.11

:quarkus-org-url: https://github.com/quarkusio
Expand Down
Loading