I have the following buil.gradle
apply plugin: "groovy"
repositories {
mavenCentral()
jcenter()
}
dependencies {
groovy group: "org.codehaus.groovy", name:"groovy-all", version: "1.8.6"
compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '2.53.0'
compile "org.testng:testng:6.3.1"
compile group: 'com.jcraft', name: 'jsch', version: '0.1.53'
compile group: 'net.schmizz', name: 'sshj', version: '0.3.1'
compile group: 'commons-lang', name: 'commons-lang', version: '2.3'
compile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.21'
compile group: 'org.bouncycastle', name: 'bcprov-jdk16', version: '1.46'
compile group: 'net.sf.expectit', name: 'expectit-core', version: '0.8.1'
compile group: 'net.sf.expectit', name: 'expectit-ant', version: '0.8.1'
compile group: 'net.sf.expectit', name: 'expectit-parent', version: '0.8.1', ext: 'pom'
compile "log4j:log4j:1.2.17"
testCompile "org.spockframework:spock-core:0.7-groovy-1.8"
testCompile "junit:junit:4.10"
testCompile "cglib:cglib-nodep:2.2.2"
testCompile "org.objenesis:objenesis:1.2"
testRuntime "org.slf4j:slf4j-api:1.7.10"
}
sourceSets {
test { groovy {
srcDir 'foo/bar/'
} }
}
buildscript {
repositories {
mavenCentral()
jcenter()
}
}
task runA << {
new GroovyShell().run(file('foo/bar/ATest.groovy'));
}
If I run gradle clean -Dtest.single=A test it works and test runs with success but if I run gradle -q runA it shows unrecognized imports like:
foo/bar/ATest.groovy: 16: unable to resolve class net.schmizz.sshj.SSHClient
# line 16, column 1.
import net.schmizz.sshj.SSHClient;
^
foo/bar/ATest.groovy: 28: unable to resolve class org.openqa.selenium.firefox.FirefoxDriver
# line 28, column 1.
import org.openqa.selenium.firefox.FirefoxDriver;
^
Here is my ATest.groovy
package foo.bar;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets;
import java.nio.file.Files
import java.nio.file.Paths;
import java.security.PublicKey
import org.apache.log4j.Logger;
import org.junit.Before
import org.junit.After
import org.junit.Test;
import org.junit.*;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Shell;
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import net.sf.expectit.Expect;
import net.sf.expectit.ExpectBuilder;
import static net.sf.expectit.filter.Filters.removeColors;
import static net.sf.expectit.filter.Filters.removeNonPrintable;
import static net.sf.expectit.matcher.Matchers.contains;
import static net.sf.expectit.matcher.Matchers.regexp;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.Select
import java.util.regex.Pattern
import javax.naming.directory.InvalidAttributesException;
import java.util.concurrent.TimeUnit
import org.apache.log4j.Logger;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
public class ATest{
private WebDriver driver;
private static final Logger logger = Logger.getLogger(ATest.class);
Properties props = new Properties();
private StringBuffer verificationErrors = new StringBuffer();
private Expect expect = null;
Session session = null;
SSHClient ssh = null;
#Before
public void setUp() throws Exception {
logger.info("========================================================================");
logger.info("Starting...");
// setting up Webdriver instance
try {
driver = new FirefoxDriver();
}
catch (Exception e) {
logger.info("Could not open Firefox instance: " + e.getMessage());
fail("Could not open Firefox instance: " + e.getMessage());
}
// setting up SSH connection
try {
ssh = new SSHClient();
ssh.addHostKeyVerifier(
new HostKeyVerifier() {
#Override
public boolean verify(String s, int i, PublicKey publicKey) {
return true;
}
});
ssh.connect(props.getProperty("host"), Integer.parseInt(props.getProperty("port")));
ssh.authPassword(props.getProperty("user"), props.getProperty("password"));
session = ssh.startSession();
session.allocateDefaultPTY();
} catch (Exception e) {
fail("Could not open SSH connection with " + props.getProperty("host") + "\nError: " + e.getMessage());
}
// start a interactive remote shell
Shell shell = session.startShell();
expect = new ExpectBuilder().withOutput(shell.getOutputStream())
.withInputs(shell.getInputStream(), shell.getErrorStream())
//.withEchoInput(System.out)
.withInputFilters(removeColors(), removeNonPrintable())
.withExceptionOnFailure()
.build();
try {
Thread.sleep(3000);
// log as 'su' user in the remote shell
expect.sendLine(props.getProperty("suuser"));
expect.expect(contains("Password: "));
expect.sendLine(props.getProperty("supassword"));
expect.expect(regexp("root#"));
}
catch (Exception e) {
fail("Error: " + e.getMessage());
}
}
#Test
public void run() {
...
}
#After
public void tearDown() throws Exception {
if (driver != null) {
driver.quit();
}
}
}
Am I missing some config?
This is expected behavior. When you run a test with gradle, it's gradle job to configure the classpath, classloader - namely, the whole environment.
If you run the the file, script on your it's your job to provide the configuration.
Have a look at GroovyShells, constructor summary. It takes a ClassLoader object and a CompileConfiguration. It's your job to provide these objects.
Related
I am trying to learn how to test custom resource watcher in the Fabric8, I follow the example from this link https://github.com/r0haaaan/kubernetes-mockserver-demo/blob/master/src/test/java/io/fabric8/demo/kubernetes/mockserver/CustomResourceMockTest.java
My custom resource is "UserACL", I am using Java junit5, this is my fabric8 version.
implementation group: 'io.fabric8', name: 'kubernetes-client', version: '5.9.0'
implementation group: 'io.fabric8', name: 'kubernetes-api', version: '3.0.12'
testImplementation group: 'io.fabric8', name: 'kubernetes-server-mock', version: '5.9.0'
This test case failed, seem there is no any WatchEvent emitted, so countLatch never count down.
Could anybody help take a look and point out what's wrong here? Anything is missing in the following code. I appreciate it in advanced.
import io.fabric8.kubernetes.api.model.Condition;
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.WatchEvent;
import io.fabric8.kubernetes.api.model.WatchEventBuilder;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.WatcherException;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import io.fabric8.kubernetes.internal.KubernetesDeserializer;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Kind;
import io.fabric8.kubernetes.model.annotation.Version;
import io.vertx.junit5.VertxExtension;
import java.net.HttpURLConnection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Rule;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
public class TestKafkaHelperUtilFourth {
#Rule
public KubernetesServer server = new KubernetesServer(true, true);
#Test
#DisplayName("Should watch all custom resources")
public void testWatch() throws InterruptedException {
// Given
server.expect().withPath("/apis/custom.example.com/v1/namespaces/default/useracls?watch=true")
.andUpgradeToWebSocket()
.open()
.waitFor(10L)
.andEmit(new WatchEvent(getUserACL("test-resource"), "ADDED"))
.waitFor(10L)
.andEmit(new WatchEventBuilder()
.withNewStatusObject()
.withMessage("410 - the event requested is outdated")
.withCode(HttpURLConnection.HTTP_GONE)
.endStatusObject()
.build()).done().always();
KubernetesClient client = server.getClient();
MixedOperation<
UserACL,
KubernetesResourceList<UserACL>,
Resource<UserACL>>
userAclClient = client.resources(UserACL.class);
// When
CountDownLatch eventRecieved = new CountDownLatch(1);
KubernetesDeserializer.registerCustomKind("custom.example.com/v1", "UserACL", UserACL.class);
Watch watch = userAclClient.inNamespace("default").watch(new Watcher<UserACL>() {
#Override
public void eventReceived(Action action, UserACL userAcl) {
if (action.name().contains("ADDED"))
eventRecieved.countDown();
}
#Override
public void onClose(WatcherException e) { }
});
// Then
eventRecieved.await(20, TimeUnit.SECONDS);
Assertions.assertEquals(0, eventRecieved.getCount());
watch.close();
}
private UserACL getUserACL(String resourceName) {
UserACLSpec spec = new UserACLSpec();
spec.setUserName("test-user-name");
UserACL createdUserACL = new UserACL();
createdUserACL.setMetadata(
new ObjectMetaBuilder().withName(resourceName).build());
createdUserACL.setSpec(spec);
Condition condition = new Condition();
condition.setMessage("Last reconciliation succeeded");
condition.setReason("Successful");
condition.setStatus("True");
condition.setType("Successful");
UserACLStatus status = new UserACLStatus();
status.setCondition(new Condition[]{condition});
createdUserACL.setStatus(status);
return createdUserACL;
}
#Group("custom.example.com")
#Version("v1")
#Kind("UserACL")
public static final class UserACL
extends CustomResource<UserACLSpec, UserACLStatus> {
}
public static final class UserACLSpec {
private String userName;
public UserACLSpec() {}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
public static final class UserACLStatus {
Condition[] condition;
public UserACLStatus() {};
public Condition[] getCondition() {
return condition;
}
public void setCondition(Condition[] condition) {
this.condition = condition;
}
}
}
I checked your code; after resolving these problems I was able to get test working:
You're setting MockServer expectations using server.expect() for mocking watch call. But you've initialized KubernetesMockServer to enable CRUD mode (this doesn't require any expectations). You need to do this instead (see false being passed as second argument which disables CRUD mode):
#Rule
public KubernetesServer server = new KubernetesServer(true, false);
In Watch expectations path I can see that UserACL resource is a namespaced one. All Namespaced scope resources in KubernetesClient must implement io.fabric8.kubernetes.api.model.Namespaced interface:
#Group("custom.example.com")
#Version("v1")
#Kind("UserACL")
public static final class UserACL extends CustomResource<UserACLSpec, UserACLStatus> implements Namespaced { }
(Optional) I'm not sure whether you're using JUnit4 or JUnit5, I also had to update org.junit.Test to org.junit.jupiter.api.Test and also add #EnableRuleMigrationSupport annotation in order to run this test on my JUnit5 project:
#EnableRuleMigrationSupport
public class TestKafkaHelperUtilFourth {
After making these changes, test seemed to run okay for me:
$ mvn -Dtest=io.fabric8.demo.kubernetes.mockserver.TestKafkaHelperUtilFourth test
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running io.fabric8.demo.kubernetes.mockserver.TestKafkaHelperUtilFourth
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
जुल॰ 08, 2022 10:29:31 अपराह्न okhttp3.mockwebserver.MockWebServer$2 execute
INFO: MockWebServer[49697] starting to accept connections
जुल॰ 08, 2022 10:29:31 अपराह्न okhttp3.mockwebserver.MockWebServer$3 processOneRequest
INFO: MockWebServer[49697] received request: GET /apis/custom.example.com/v1/namespaces/default/useracls?watch=true HTTP/1.1 and responded: HTTP/1.1 101 Switching Protocols
जुल॰ 08, 2022 10:29:31 अपराह्न okhttp3.mockwebserver.MockWebServer$2 acceptConnections
INFO: MockWebServer[49697] done accepting connections: Socket closed
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.779 s - in io.fabric8.demo.kubernetes.mockserver.TestKafkaHelperUtilFourth
I'm trying to get a basic "httpclient" "httprequest" "httpresponse" working with Resilience4j Retry.
The verbatim code from : https://resilience4j.readme.io/docs/retry
RetryConfig config = RetryConfig.custom()
.maxAttempts(5)
.waitDuration(Duration.ofMillis(1000))
.retryOnResult(response -> response.getStatus() == 500)
.retryOnException(e -> e instanceof WebServiceException)
.retryExceptions(IOException.class, TimeoutException.class)
.ignoreExceptions(BusinessException.class, OtherBusinessException.class)
.build();
// Create a RetryRegistry with a custom global configuration
RetryRegistry registry = RetryRegistry.of(config);
// Get or create a Retry from the registry -
// Retry will be backed by the default config
Retry retryWithDefaultConfig = registry.retry("name1");
Note, their code above misses defining the generic "T", like this:
RetryConfig config = RetryConfig.<MyConcrete>custom()
and the verbatim code from : https://resilience4j.readme.io/docs/examples
Supplier<String> supplierWithResultAndExceptionHandler = SupplierUtils
.andThen(supplier, (result, exception) -> "Hello Recovery");
Supplier<HttpResponse> supplier = () -> httpClient.doRemoteCall();
Supplier<HttpResponse> supplierWithResultHandling = SupplierUtils.andThen(supplier, result -> {
if (result.getStatusCode() == 400) {
throw new ClientException();
} else if (result.getStatusCode() == 500) {
throw new ServerException();
}
return result;
});
HttpResponse httpResponse = circuitBreaker
.executeSupplier(supplierWithResultHandling);
======
So using those 2 "partials" , I've come up with this.
Note, I am using some "real" java.net.http.HttpClient and java.net.http.HttpResponse (from JDK11)
import io.github.resilience4j.core.SupplierUtils;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.retry.RetryRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import javax.inject.Inject;
import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
public final class ResilientHttpClient /* implements IResilientHttpClient */ {
private static Logger logger;
private final HttpClient httpClient;
#Inject
public ResilientHttpClient(final HttpClient httpClient) {
this(LoggerFactory
.getLogger(ResilientHttpClient.class), httpClient);
}
/**
* Constructor, which pre-populates the provider with one resource instance.
*/
public ResilientHttpClient(final Logger lgr,
final HttpClient httpClient) {
if (null == lgr) {
throw new IllegalArgumentException("Logger is null");
}
this.logger = lgr;
if (null == httpClient) {
throw new IllegalArgumentException("HttpClient is null");
}
this.httpClient = httpClient;
}
public String executeHttpRequest(String circuitbreakerInstanceName, HttpRequest httpRequest) {
try {
/* circuitbreakerInstanceName is future place holder for .yml configuration see : https://resilience4j.readme.io/docs/getting-started-3 */
RetryConfig config = RetryConfig.<HttpResponse>custom()
.waitDuration(Duration.ofMillis(1000))
.retryOnResult(response -> response.statusCode() == 500)
.retryOnException(e -> e instanceof ArithmeticException)
.retryExceptions(IOException.class, TimeoutException.class)
//.ignoreExceptions(BusinessException.class, OtherBusinessException.class)
.build();
// Create a RetryRegistry with a custom global configuration
RetryRegistry registry = RetryRegistry.of(config);
// Get or create a Retry from the registry -
// Retry will be backed by the default config
Retry retryWithDefaultConfig = registry.retry(circuitbreakerInstanceName);
Supplier<HttpResponse> supplier = () -> this.httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
Supplier<String> supplierWithResultAndExceptionHandler = SupplierUtils
.andThen(supplier, (result, exception) -> "Hello Recovery");
Supplier<HttpResponse> supplierWithResultHandling = SupplierUtils.andThen(supplier, result -> {
if (result.statusCode() == HttpStatus.BAD_REQUEST.value()) {
throw new RuntimeException("400");
} else if (result.statusCode() == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
throw new RuntimeException("500");
}
return result;
});
HttpResponse<String> response = retryWithDefaultConfig.executeSupplier(supplierWithResultHandling);
String responseBody = response.body();
return responseBody;
} catch (Exception ex) {
throw new RuntimeException((ex));
}
}
}
The issue I am having is:
The line:
Supplier<HttpResponse> supplier = () - > this.httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
is giving an error (in intelliJ) of "unhandled exceptions" "IOException, InterruptedException"
So modifying the method to be:
public String executeHttpRequest(String circuitbreakerInstanceName, HttpRequest httpRequest) throws IOException, InterruptedException {
"feels wrong". But even when I try it...it doesn't resolve anything. :(
It is probably some lamda checked-exception voodoo.
But more to the point:
So I don't know if the way I've brought together the 2 partials is even correct. The samples are a little lacking in the fully-working area.
Thank for any help. Getting a basic httpclient "retry" a few times shouldn't be too hard. But I'm hitting my head against the wall.
My gradle dependencies.
dependencies {
implementation group: 'javax.inject', name: 'javax.inject', version: javaxInjectVersion
implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion
implementation group: 'org.springframework', name: 'spring-web', version: springWebVersion
implementation "io.github.resilience4j:resilience4j-circuitbreaker:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-ratelimiter:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-retry:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-bulkhead:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-cache:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-timelimiter:${resilience4jVersion}"
testCompile group: 'junit', name: 'junit', version: junitVersion
}
and
resilience4jVersion = '1.5.0'
slf4jVersion = "1.7.30"
javaxInjectVersion = "1"
springWebVersion = '5.2.8.RELEASE'
junitVersion = "4.12"
just out of interest:
Which Java version are you using? Java 11?
Why can't you use Spring Boot? The Resilience4j Spring Boot starter simplifies the configuration a lot.
If you configure retryOnResult(response -> response.getStatus() == 500), you don't have to use SupplierUtils anymore to map a HttpResponse with a certain status code to a runtime exception.
RetryConfig config = RetryConfig.<HttpResponse<String>>custom()
.waitDuration(Duration.ofMillis(1000))
.retryOnResult(response -> response.statusCode() == 500)
.retryExceptions(IOException.class, TimeoutException.class)
.build();
Please don't create Registries and Configs inside of executeHttpRequest, but inject them into your Constructor.
You can create a static method like this:
public static <T> HttpResponse<T> executeHttpRequest(Callable<HttpResponse<T>> callable, Retry retry, CircuitBreaker circuitBreaker) throws Exception {
return Decorators.ofCallable(callable)
.withRetry(retry)
.withCircuitBreaker(circuitBreaker)
.call();
}
and invoke the method as follows:
HttpResponse<String> response = executeHttpRequest(
() -> httpClient.send(request, HttpResponse.BodyHandlers.ofString()),
retry,
circuitBreaker);
I am following a tutorial to make a basic Discord Bot, (https://medium.com/discord-bots/making-a-basic-discord-bot-with-java-834949008c2b) and i got the error, Could not set unknown property 'mainClassName' for root project 'Blue Bot' of type org.gradle.api.Project and I don't know how to fix it. Here is my build.grade code
plugins {
id 'java'
}
mainClassName = "Main"
group 'BlueBot'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'net.dv8tion:JDA:4.0.0_62'
}
and here is my Main.java code
import net.dv8tion.jda.api.AccountType;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import javax.security.auth.login.LoginException;
public class Main extends ListenerAdapter {
public static void main(String[] args) throws LoginException {
JDABuilder builder = new JDABuilder(AccountType.BOT);
String token = "enter token here";
builder.setToken(token);
builder.addEventListeners(new Main());
builder.build();
}
#Override
public void onMessageReceived(MessageReceivedEvent event) {
System.out.println("We received a message from " +
event.getAuthor().getName() + ": " +
event.getMessage().getContentDisplay()
);
if (event.getMessage().getContentRaw().equals("I am lonely")) {
event.getChannel().sendMessage("Who isn't?").queue();
}
}
}
Let me know if you need more info.
You need to apply the application plugin:
plugins {
id 'application'
}
To the enum field of my entity I have added #CassandraType(type = DataType.Name.INT). However not the ordinal of the enum, but the string representation instead, is used in the statement sent to Cassandra. Thus I get the following error:
org.springframework.data.cassandra.CassandraInvalidQueryException: SessionCallback; CQL [INSERT INTO thing (thing_id,some_enum) VALUES (1,'Foo');]; Expected 4 or 0 byte int (3); nested exception is com.datastax.driver.core.exceptions.InvalidQueryException: Expected 4 or 0 byte int (3)
Below you can find a minimal example, reproducing the problem.
What am I doing wrong?
test/src/main/kotlin/enumtest/Application.kt
package enumtest
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
#SpringBootApplication
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
test/src/main/kotlin/enumtest/SomeEnum.kt
package enumtest
enum class SomeEnum {
Foo,
Bar
}
test/src/main/kotlin/enumtest/Thing.kt
package enumtest
import com.datastax.driver.core.DataType
import org.springframework.data.cassandra.core.cql.PrimaryKeyType
import org.springframework.data.cassandra.core.mapping.CassandraType
import org.springframework.data.cassandra.core.mapping.Column
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn
import org.springframework.data.cassandra.core.mapping.Table
#Table("thing")
#Suppress("unused")
class Thing(
#PrimaryKeyColumn(name = "thing_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
var thingId: Long,
#CassandraType(type = DataType.Name.INT)
#Column("some_enum")
var someEnum: SomeEnum
)
test/src/main/kotlin/enumtest/ThingRepository.kt
package enumtest
import org.springframework.data.cassandra.repository.CassandraRepository
import org.springframework.stereotype.Repository
#Repository
interface ThingRepository : CassandraRepository<Thing, Long>
test/src/main/resources/application.yml
spring:
data:
cassandra:
contact-points: localhost
port: 9142
keyspace_name: enumtest
test/src/test/kotlin/enumtest/PersistenceTest.kt
package enumtest
import org.cassandraunit.spring.CassandraDataSet
import org.cassandraunit.spring.CassandraUnitDependencyInjectionTestExecutionListener
import org.cassandraunit.spring.EmbeddedCassandra
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.TestExecutionListeners
import org.springframework.test.context.junit4.SpringRunner
#RunWith(SpringRunner::class)
#SpringBootTest
#TestExecutionListeners(
listeners = [CassandraUnitDependencyInjectionTestExecutionListener::class],
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
#CassandraDataSet(value = ["cql/cassandra_schema.cql"], keyspace = "enumtest")
#EmbeddedCassandra
class PersistenceTest {
#Autowired
lateinit var thingRepository: ThingRepository
#Test
fun `test save`() {
thingRepository.save(Thing(1, SomeEnum.Foo))
val things = thingRepository.findAll()
Assert.assertEquals(1, things.size)
val thing = things[0]
Assert.assertEquals(SomeEnum.Foo, thing.someEnum)
}
}
test/src/test/resources/cql/cassandra_schema.cql
CREATE KEYSPACE IF NOT exists enumtest
WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':1};
CREATE TABLE IF NOT exists enumtest.thing (
thing_id bigint,
some_enum int,
PRIMARY KEY (thing_id)
);
test/build.gradle
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'org.jetbrains.kotlin.jvm' version '1.3.30'
id 'org.jetbrains.kotlin.plugin.spring' version '1.3.30'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
maven { url "https://repository.apache.org/snapshots/" }
}
dependencies {
implementation group: 'org.springframework.boot', name: 'spring-boot-starter'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-cassandra'
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8'
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect'
testImplementation group: 'org.cassandraunit', name: 'cassandra-unit-spring', version: '3.5.0.1'
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
}
compileKotlin {
kotlinOptions {
freeCompilerArgs = ['-Xjsr305=strict']
jvmTarget = '1.8'
}
}
compileTestKotlin {
kotlinOptions {
freeCompilerArgs = ['-Xjsr305=strict']
jvmTarget = '1.8'
}
}
Here is the full version of the minimal example as a download to faciliate experimentation: https://drive.google.com/open?id=1zzIDhbWycaj4WXrze2sAmw8xRPacA8Js
Edit: Since it seems to be a bug, I just opened a Jira issue.
I've been trying to get this working for quite awhile and it seems I finally got it!
I was running into the same issue you were with the codec...I have no idea why that's not working. According to their documentation you were doing it exactly right.
So I implemented my own Cassandra Write Converter. See below
#Configuration
class CassandraConfig(val cluster: Cluster){
#Bean
fun setCustomCassandraConversions() = CassandraCustomConversions(listOf(EnumWriteConverter.INSTANCE, EnumReadConverter.INSTANCE))
#WritingConverter
enum class EnumWriteConverter : Converter<Enum<MyEnum>, Int> {
INSTANCE;
override fun convert(source: Enum<MyEnum>) = source.ordinal
}
#ReadingConverter
enum class EnumReadConverter : Converter<Int, Enum<MyEnum>> {
INSTANCE;
override fun convert(source: Int) = MyEnum.values()[source]
}
}
This should on every write you do to Cassandra convert all enums it sees of type MyEnum to an Int using the overridden converter. This opens you up to the possibility of having multiple of these for different types of Enums where maybe for some reason you would like to write other custom values from them instead of always converting all enums.
Hope this works!
EDIT
Note the change in removing { } for INSTANCE on each converter, and registering the ReadingConverter with the CassandraCustomConversions
This is fixed since Spring Boot version 2.1.5.
However, the #CassandraType needs to be placed at the getter explicitly in Kotlin, because otherwise it is not seen at runtime.
In practice this simply means replacing this:
#CassandraType(type = DataType.Name.INT)
var someEnum: SomeEnum
with that:
#get: CassandraType(type = DataType.Name.INT)
var someEnum: SomeEnum
I have modified an existing Jetty project and after I builded, I got 404. Maybe I need to modify other files which I do not know.
I am using gradle to build. Here is the build.gradle :
apply plugin: 'java'
apply plugin: 'jetty'
apply plugin: 'eclipse'
repositories {
mavenCentral()
}
dependencies {
compile 'org.springframework:spring-webmvc:4.1.6.RELEASE'
compile 'org.hibernate:hibernate-core:4.3.6.Final'
testImplementation 'junit:junit:4.12'
testCompile 'junit:junit:4.11'
testCompile 'org.hamcrest:hamcrest-all:1.3'
compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.14'
}
test {
exclude '**/*IntegrationTest*'
}
task integrationTest(type: Test) {
include '**/*IntegrationTest*'
doFirst {
jettyRun.contextPath = '/';
jettyRun.httpPort = 8080 // Port for test
jettyRun.daemon = true
jettyRun.execute()
}
doLast {
jettyStop.stopPort = 8091 // Port for stop signal
jettyStop.stopKey = 'stopKey'
jettyStop.execute()
}
}
// Embeded Jetty for testing
jettyRun{
contextPath = "spring4"
httpPort = 8080
}
jettyRunWar{
contextPath = "spring4"
httpPort = 8080
}
//For Eclipse IDE only
eclipse {
wtp {
component {
//define context path, default to project folder name
contextPath = 'spring4'
}
}
}
Here the other class:
package com.hello.webapp;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import com.hello.service.SignUpService;
#Path("/signUp")
public class SignUpWebapp {
private static SignUpService signUpService = new SignUpService();
#GET()
public String hello() {
return signUpService.sayHello();
}
}
here the simple service:
package com.hello.service;
public class SignUpService {
public String sayHello() {
return "signUp";
}
}
this is another the integration test class
package com.hello.webapp;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import org.junit.Test;
public class SignUpIntegrationTest {
private static String SIGNUP = "http://localhost:8080/signUp";
#Test
public void testHello() throws Exception {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(SIGNUP);
String response = webTarget.request().get(String.class);
assertThat(response, is("signUp"));
}
}
So, when I run gradle integrationTest I get an error saying that Jetty is already running. And when I try to visit localhost/signUp I get a 404.
One solution is to use gretty instead:
apply plugin: 'org.akhikhl.gretty'
....
classpath 'org.akhikhl.gretty:gretty:1.4.0'
...
gretty {
port = 8083
contextPath = '/'
servletContainer = 'jetty9'
jvmArgs = [
'-Xms700m',
'-Xmx2048m'
]
loggingLevel='INFO'
debugPort = 5005 // default
debugSuspend = true // default
httpsEnabled = true
httpsPort = 8443
sslKeyStorePath = 'some.jks'
sslKeyStorePassword = 'somepwd'
}
and then gradle appStart
Let me know if that helps.