When working with azure BlobStorage I'm quite new to this topic but I managed to get it working in java. So we have some xml files saved there and collect the file list as strings. Now I've tried to create a unit tests to verify it stays working and since the getFiles() function is a very small I expected it to be very simple to test.
#Override
public List<String> getFiles(ExecutionContext context) {
return StreamSupport.stream(blobContainerClient.listBlobs().spliterator(), true)
.map(BlobItem::getName)
.collect(Collectors.toList());
}
I can mock the com.azure.storage.blob.blobContainerClient and its function listBlobs, but when trying to create the PagedIterable from a simple List I cannot make it fit the right data types or it runs into an endless loop.
Since the functionality is so minimal, we would just skip to test this, but ou of curiosity I just want to know if it could be tested or what is wrong with my code:
import com.azure.core.http.rest.*;
import com.azure.core.util.IterableStream;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.models.BlobItem;
import com.microsoft.azure.functions.ExecutionContext;
import lombok.SneakyThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.CoreSubscriber;
import reactor.core.Fuseable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Supplier;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;
class BlobstoreConnectorListFilesTest {
private final BlobContainerClient blobContainerClientMock = mock(BlobContainerClient.class);
private final ExecutionContext context = mock(ExecutionContext.class);
private final String id1 = UUID.randomUUID().toString();
private final String id2 = UUID.randomUUID().toString();
#BeforeEach
void setUp() {
BlobItem item1 = mock(BlobItem.class);
when(item1.getName()).thenReturn(id1 + ".xml");
BlobItem item2 = mock(BlobItem.class);
when(item2.getName()).thenReturn(id2 + ".xml");
List<BlobItem> arrayList = new ArrayList<>();
arrayList.add(item1);
arrayList.add(item2);
Mono<PagedResponse<BlobItem>> monoSource = new Mono<>() {
private final Page<BlobItem> page = new Page<>() {
#Override
public IterableStream<BlobItem> getElements() {
return new IterableStream<>(Flux.fromIterable(arrayList));
}
#Override
public String getContinuationToken() {
return null;
}
};
final PagedResponseBase<String, BlobItem> pagedResponseBase = new PagedResponseBase<>(null, 200, null, page
, null);
final Fuseable.QueueSubscription<BlobItem> fuseableQueueSubscription = new Fuseable.QueueSubscription<>() {
#Override
public void request(long l) {
}
#SneakyThrows
#Override
public void cancel() {
throw new InterruptedException();
}
#Override
public int size() {
return arrayList.size();
}
#Override
public boolean isEmpty() {
return arrayList.isEmpty();
}
#Override
public void clear() {
arrayList.clear();
}
#Override
public BlobItem poll() {
var value = arrayList.stream().findFirst().orElse(null);
if(value!=null){
arrayList.remove(value);
}
return value;
}
#Override
public int requestFusion(int i) {
return 0;
}
};
#Override
public void subscribe(CoreSubscriber<? super PagedResponse<BlobItem>> coreSubscriber) {
coreSubscriber.onNext(pagedResponseBase);
coreSubscriber.onSubscribe(fuseableQueueSubscription);
}
};
Supplier<Mono<PagedResponse<BlobItem>>> blobItemSupplier = () -> monoSource;
PagedFlux<BlobItem> pagedFlux = new PagedFlux<>(blobItemSupplier);
PagedIterable<BlobItem> leaflets = new PagedIterable<>(pagedFlux);
doReturn(leaflets).when(blobContainerClientMock).listBlobs();
}
#Test
void getAllFiles() {
BlobstoreConnector connector = new BlobstoreConnector(blobContainerClientMock);
List<String> actual = connector.getFiles(context);
assertEquals(2, actual.size());
assertTrue(actual.stream().anyMatch(fileName -> fileName.equals(id1 + ".xml")));
assertTrue(actual.stream().anyMatch(fileName -> fileName.equals(id2 + ".xml")));
}
}
Related
I am trying to build a Kafka source connector for audio files. from my understanding, I have to read the audio files as a Byte array.
I am using the confluent-quick-start project as a skeleton for development. the connector is not working, I can't tell why, because I don't know how to make it print logs for errors. I need help to make this work, I am not an expert in java, you can probably tell by the code.
is my approach correct? and do I have to do anything to the pom.xml file or just leave it as is?
I have examined previously available projects and tried to apply the concept for audio files. the following are the classes:
AudioSourceConnectorConfig
package org.othman.example;
import org.apache.kafka.common.config.AbstractConfig;
import org.apache.kafka.common.config.ConfigDef;
import org.apache.kafka.common.config.ConfigDef.Type;
import org.apache.kafka.common.config.ConfigDef.Importance;
import java.util.Map;
public class AudioSourceConnectorConfig extends AbstractConfig {
public static final String FILENAME_CONFIG="fileName";
private static final String FILENAME_DOC ="Enter the name of the audio file";
public static final String TOPIC_CONFIG = "topic";
private static final String TOPIC_DOC = "Enter the topic to write to..";
public AudioSourceConnectorConfig(ConfigDef config, Map<String, String> parsedConfig) {
super(config, parsedConfig);
}
public AudioSourceConnectorConfig(Map<String, String> parsedConfig) {
this(conf(), parsedConfig);
}
public static ConfigDef conf() {
return new ConfigDef()
.define(FILENAME_CONFIG, Type.STRING, Importance.HIGH, FILENAME_DOC)
.define(TOPIC_CONFIG, Type.STRING, Importance.HIGH, TOPIC_DOC);
}
public String getFilenameConfig(){
return this.getString("fileName");
}
public String getTopicConfig(){
return this.getString("topic");
}
}
AudioSourceConnector
package org.othman.example;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.kafka.common.config.ConfigDef;
import org.apache.kafka.connect.connector.Task;
import org.apache.kafka.connect.source.SourceConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AudioSourceConnector extends SourceConnector {
private static Logger log = LoggerFactory.getLogger(AudioSourceConnector.class);
private AudioSourceConnectorConfig config;
private Map<String, String> configProps;
#Override
public String version() {
return VersionUtil.getVersion();
}
#Override
public void start(Map<String, String> map) {
//config = new AudioSourceConnectorConfig(map);
this.configProps = new HashMap(map);
//TODO: Add things you need to do to setup your connector.
}
#Override
public Class<? extends Task> taskClass() {
//TODO: Return your task implementation.
return AudioSourceTask.class;
}
#Override
public List<Map<String, String>> taskConfigs(int maxTasks) {
return (List) IntStream.range(0, maxTasks).mapToObj((i) -> {
return new HashMap(this.configProps);
}).collect(Collectors.toList());
//TODO: Define the individual task configurations that will be executed.
}
#Override
public void stop() {
//TODO: Do things that are necessary to stop your connector.
this.configProps=null;
}
#Override
public ConfigDef config() {
return AudioSourceConnectorConfig.conf();
}
}
AudioSourceTask
package org.othman.example;
import org.apache.kafka.connect.data.Schema;
import org.apache.kafka.connect.source.SourceRecord;
import org.apache.kafka.connect.source.SourceTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class AudioSourceTask extends SourceTask {
static final Logger log = LoggerFactory.getLogger(AudioSourceTask.class);
AudioSourceConnectorConfig config;
private Process inputProcess;
byte [] audioFile;
#Override
public String version() {
return VersionUtil.getVersion();
}
#Override
public void start(Map<String, String> map) {
//TODO: Do things here that are required to start your task.
// This could be open a connection to a database, etc.
this.config = new AudioSourceConnectorConfig(map);
try{
audioFile = Files.readAllBytes(Paths.get(this.config.getFilenameConfig()));
// this.inputProcess = (new ProcessBuilder((new String[]{this.config.getFilenameConfig()}))).redirectError().start();
}
catch(IOException e){
System.out.println("ERROR WHILE TRYING TO READ AUDIO FILE...");
e.printStackTrace();
}
}
#Override
public List<SourceRecord> poll() throws InterruptedException {
//TODO: Create SourceRecord objects that will be sent the kafka cluster.
final ArrayList<SourceRecord> records = new ArrayList<>();
SourceRecord record;
for (int i=0;i < audioFile.length - 1;i++) {
record= new SourceRecord(null, null, this.config.getTopicConfig(), 0, Schema.BYTES_SCHEMA, audioFile[i]);
records.add(record);
}
return records;
}
#Override
public void stop() {
//TODO: Do whatever is required to stop your task.
}
}
Hi community: Trying to integrate Cucumber with JUnit, I have the next class.
import cl.cukes.ParameterizedRunnerFactory;
import cl.test.AutomatedWebTest;
import cl.util.report.Report;
import cucumber.annotation.en.Given;
import cucumber.annotation.en.Then;
import cucumber.annotation.en.When;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;
import org.junit.runner.Runner;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.UseParametersRunnerFactory;
import org.junit.runners.model.InitializationError;
import org.junit.runners.parameterized.TestWithParameters;
import org.openqa.selenium.By;
import org.openqa.selenium.ie.InternetExplorerDriver;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
//JAVA
#RunWith(Parameterized.class)
#UseParametersRunnerFactory(ParameterizedRunnerFactory.class)
public class CucumberStepDefs extends AutomatedWebTest {
#Parameterized.Parameter(value = 0)
public String user;
#Parameterized.Parameter(value = 1)
public String pass;
public static class CucumberRunnerFactor extends ParameterizedRunnerFactory {
public Runner createRunnerForTestWithParameters(TestWithParameters test)
throws InitializationError {
try {
return new Cucumber(test.getTestClass().getJavaClass());
} catch (IOException e) {
throw new InitializationError(e);
}
}
}
#Given("^Enter to the QA URL environment")
public void Login() throws Exception {
baseUrl = "http://www.miprivado.cl/";
driver = new InternetExplorerDriver();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.get(baseUrl + "ucachaca/test.htm");
driver.switchTo().defaultContent();
driver.switchTo().frame("frmbody");
}
#When("^Fields Username y Passwordare displayed so enter parameters$")
public void UsuarioPass() throws Exception {
driver.findElement(By.id("TxbTELLUSERID")).clear();
driver.findElement(By.id("TxbTELLUSERID")).sendKeys(user);
driver.findElement(By.id("TxbUSERPASSW")).clear();
driver.findElement(By.id("TxbUSERPASSW")).sendKeys(pass);
screenshot.take(this, driver, "LoginR C01");
driver.findElement(By.id("BtnSubmit")).click();
driver.switchTo().defaultContent();
driver.switchTo().frame("frmwebteller");
driver.manage().window().maximize();
}
#Then("^User gets into the system and do Logout Logout$")
public void Logout() throws Exception {
screenshot.take (this, driver, "LoginR C02");
driver.switchTo ().defaultContent ();
driver.switchTo ().frame ("frmbody").switchTo ().frame ("menu");
driver.findElement(By.cssSelector("b")).click();
screenshot.take (this, driver, "LoginR C03");
driver.findElement(By.linkText("Log Off")).click();
screenshot.take (this, driver, "LoginR C04");
driver.quit();
}
}
The ParameterizedRunnerFactory is the next:
import org.junit.runner.Runner;
import org.junit.runners.model.InitializationError;
import org.junit.runners.parameterized.ParametersRunnerFactory;
import org.junit.runners.parameterized.TestWithParameters;
/**
*
*/
public class ParameterizedRunnerFactory implements ParametersRunnerFactory {
#Override
public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
return new ParameterizedRunner(test);
}
}
The parameterized Runner class is the next:
import org.junit.After;
import org.junit.Before;
import org.junit.runners.model.*;
import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters;
import org.junit.runners.parameterized.TestWithParameters;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class ParameterizedRunner extends BlockJUnit4ClassRunnerWithParameters {
public ParameterizedRunner(TestWithParameters test) throws InitializationError {
super(test);
}
// workaround for: https://github.com/junit-team/junit/issues/1046
private static final ConcurrentHashMap<Class<?>, TestClass> testClasses = new ConcurrentHashMap<>();
#Override
protected TestClass createTestClass(Class<?> clazz) {
TestClass testClass = testClasses.get(clazz);
if (testClass == null) {
testClasses.put(clazz, testClass = new TestClass(clazz));
}
return testClass;
}
// playing whack-a-mole with new TLAB allocations by re-defining with{Befores,Afters}...
#Override
protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
List<FrameworkMethod> list = getTestClass().getAnnotatedMethods(Before.class);
if (list.isEmpty()) {
return statement;
}
return new BeforesStatement(target, statement, list);
}
#Override
protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) {
List<FrameworkMethod> list = getTestClass().getAnnotatedMethods(After.class);
if (list.isEmpty()) {
return statement;
}
return new AftersStatement(target, statement, list);
}
private static final class BeforesStatement extends Statement {
private static final Object[] EMPTY_ARGS = new Object[0];
private final Object target;
private final Statement statement;
private final List<FrameworkMethod> list;
BeforesStatement(Object target, Statement statement, List<FrameworkMethod> list) {
this.target = target;
this.statement = statement;
this.list = list;
}
#Override
public void evaluate() throws Throwable {
// (1) Avoid ImmutableCollections#iterator()
for (int i = 0, size = list.size(); i < size; ++i) {
list.get(i).invokeExplosively(target, EMPTY_ARGS);
}
statement.evaluate();
}
}
private static final class AftersStatement extends Statement {
private static final Object[] EMPTY_ARGS = new Object[0];
private final Object target;
private final Statement statement;
private final List<FrameworkMethod> list;
AftersStatement(Object target, Statement statement, List<FrameworkMethod> list) {
this.target = target;
this.statement = statement;
this.list = list;
}
#Override
public void evaluate() throws Throwable {
// (2) Lazily create ArrayList
ArrayList<Throwable> throwables = null;
try {
statement.evaluate();
} catch (Throwable e) {
throwables = new ArrayList<Throwable>();
throwables.add(e);
} finally {
for (int i = 0, size = list.size(); i < size; ++i) {
try {
list.get(i).invokeExplosively(target, EMPTY_ARGS);
} catch (Throwable e) {
if (throwables == null) {
throwables = new ArrayList<Throwable>();
}
throwables.add(e);
}
}
}
if (throwables != null) {
MultipleFailureException.assertEmpty(throwables);
}
}
}
}
The Cucumber Runner Test class is the next:
import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(features={"//src/features/Login.feature"}
,format = {"pretty", "html:target/cucumber"}
,glue = {"cucumber.CucumberStepDefs"}
)
/*#Suite.SuiteClasses({
CucumberStepDefs.class,
})*/
public class CucumberRunnerTest {
}
When I run my CucumberStepDefs, it displays the next issue on IntelliJ:
java.lang.Exception: No public static parameters method on class cl.cucumber.CucumberStepDefs
All the classes don't show error, but I'm not able to run this class.
Could anybody help me with this?
Don't try to mix JUnit with Maven:
The best answer were done by Grasshopper:
1.- Create the Feature file.
2.- Use Scenario Outline and put the data in Examples section.
3.- Use a runner like next:
#CucumberOptions(features = {"src/test/resource/features"},
glue={"stepdefs"},
monochrome = true,
tags = {"~#Ignore"},
plugin = {"pretty","html:target/cucumber-reports/cucumber-pretty",
"json:target/cucumber-reports/CucumberTestReport.json",
"rerun:target/cucumber-reports/rerun.txt",
"usage:target/cucumber-usage.json"}
)
public class TestRunner extends ExtendedTestNGRunner{
private TestNGCucumberRunner testNGCucumberRunner;
#BeforeClass(alwaysRun = true)
public void setUpClass() throws Exception {
testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
}
#Test(groups = "cucumber", description = "Runs Cucumber Feature", dataProvider = "features")
public void feature(CucumberFeatureWrapper cucumberFeature) {
testNGCucumberRunner.runCucumber(cucumberFeature.getCucumberFeature());
}
#DataProvider
public Object[][] features() {
return testNGCucumberRunner.provideFeatures();
}
#AfterClass(alwaysRun = true)
public void tearDownClass() throws Exception {
testNGCucumberRunner.finish();
}
}
And run the Feature.
Thanks a lot, Grasshopper!!!
I am writing data to accumulo storage natively using Geomesa Native Client. Here is my java code
package org.locationtech.geomesa.api;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.mock.MockInstance;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.security.Authorizations;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.junit.Assert;
import org.junit.Test;
import org.locationtech.geomesa.accumulo.data.AccumuloDataStore;
import org.locationtech.geomesa.accumulo.index.AccumuloFeatureIndex;
import org.locationtech.geomesa.accumulo.index.AccumuloFeatureIndex$;
import org.locationtech.geomesa.utils.index.IndexMode$;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.filter.FilterFactory2;
import javax.annotation.Nullable;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
public class WorkerBeta {
public static void main(String[] args){
try {
DomainObjectValueSerializer dovs = new DomainObjectValueSerializer();
final GeoMesaIndex<DomainObject> index = AccumuloGeoMesaIndex.buildWithView(
"aj_v14",
"localhost:2181",
"hps",
"root", "9869547580",
false,
dovs,
new SimpleFeatureView<DomainObject>() {
AttributeTypeBuilder atb = new AttributeTypeBuilder();
private List<AttributeDescriptor> attributeDescriptors =
Lists.newArrayList(atb.binding(Integer.class).buildDescriptor("rId")
, atb.binding(String.class).buildDescriptor("dId")
, atb.binding(Integer.class).buildDescriptor("s")
, atb.binding(Integer.class).buildDescriptor("a")
, atb.binding(Integer.class).buildDescriptor("e")
);
#Override
public void populate(SimpleFeature f, DomainObject domainObject, String id, byte[] payload, Geometry geom, Date dtg) {
f.setAttribute("rId", domainObject.rideId);
f.setAttribute("dId", domainObject.deviceId);
f.setAttribute("s", domainObject.speed);
f.setAttribute("a", domainObject.angle);
f.setAttribute("e", domainObject.error);
}
#Override
public List<AttributeDescriptor> getExtraAttributes() {
return attributeDescriptors;
}
}
);
//Inserting
final DomainObject one = new DomainObject(1, "AJJASsP", 12, 40, 1);
final GeometryFactory gf = JTSFactoryFinder.getGeometryFactory();
System.out.println(index.insert(
one,
gf.createPoint(new Coordinate(-74.0, 34.0)),
date("2017-03-31T01:15:00.000Z")
));
//Read
GeoMesaQuery q = GeoMesaQuery.GeoMesaQueryBuilder.builder()
.within(-90.0, -180, 90, 180)
.during(date("2017-01-01T00:00:00.000Z"), date("2017-04-01T00:00:00.000Z"))
.build();
Iterable<DomainObject> results = index.query(q);
int counter = 0;
for(DomainObject dm : results){
counter += 1;
System.out.println("result counter: " + counter);
dovs.toBytes(dm);
}
}
catch (Exception ex){
ex.printStackTrace();
}
index.close();
}
public static class DomainObject {
public final int rideId;
public final String deviceId;
public final int angle;
public final int speed;
public final int error;
public DomainObject(int rideId, String deviceId, int angle, int speed, int error) {
this.rideId = rideId;
this.deviceId = deviceId;
this.angle = angle;
this.speed = speed;
this.error = error;
}
}
public static class DomainObjectValueSerializer implements ValueSerializer<DomainObject> {
public static final Gson gson = new Gson();
#Override
public byte[] toBytes(DomainObject o) {
return gson.toJson(o).getBytes();
}
#Override
public DomainObject fromBytes(byte[] bytes) {
return gson.fromJson(new String(bytes), DomainObject.class);
}
}
public static Date date(String s) {
return Date.from(ZonedDateTime.parse(s).toInstant());
}
}
The problem with this code is, I need to create index object every time for a new insert request and call index.close() to reflect the same. But I can't execute insert() agin, once index.close() is called. However i will be accepting insert request from queue at very high rate and I don't want to create index object every time. How can i do that?
In short how i can flush writes without calling close().
I created geomesa Client class file to use geomesa natively. Below is the partial implementation of the same which shows how you can flush with AccumuloAppendFeatureWriter without calling to close.
public class GeomesaClient {
private AccumuloDataStore ds = null;
private AccumuloAppendFeatureWriter fw = null;
private SimpleFeatureSource sfs = null;
private String tableName = "";
private FeatureStore fst = null;
private SimpleFeatureType sft;
public GeomesaClient(Map<String, String> dsConf) throws Exception {
this.ds = (AccumuloDataStore) DataStoreFinder.getDataStore(dsConf);
this.tableName = dsConf.get("tableName");
sft = createFeatureType();
if(!Arrays.asList(this.ds.getTypeNames()).contains(sft.getTypeName())){
ds.createSchema(sft);
}
this.fst = (FeatureStore)sfs;
this.fw = (AccumuloAppendFeatureWriter) (this.ds.getFeatureWriterAppend(sft.getTypeName(),
Transaction.AUTO_COMMIT));
this.sfs = ds.getFeatureSource(sft.getTypeName());
}
/*
Flush with AccumuloAppendFeatureWriter
*/
public void flush(boolean force) {
fw.flush();
}
}
I need help creating a null-safe BooleanBinding. It has to be null-safe since I can not provide default values for all attributes in the model (one reason: the model contains enumerations). My first approach has been as follows:
executeButtonDisabled.bind(missionProperty().isNotNull().and(missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.CREATED)));
final BooleanBinding isNotExecutingBinding = missionProperty().isNotNull().and(missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.EXECUTING));
completeButtonDisabled.bind(isNotExecutingBinding);
cancelButtonDisabled.bind(isNotExecutingBinding)
But that approach does not work because the complete expression is evaluated which results in a NullPointerException (but it correctly updates the buttons, when a property is provided). Now I am trying to use the Bindings class as suggested in JavaFX binding and null values, but I can't make it work. Here's my current approach:
final BooleanBinding isNotCreatedBinding = Bindings.createBooleanBinding(
() -> mission.isNull().getValue()
? true
: missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.CREATED).getValue());
final BooleanBinding isNotExecutingBinding = Bindings.createBooleanBinding(
() -> mission.isNull().getValue()
? true
: missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.EXECUTING).getValue());
executeButtonDisabled.bind(isNotCreatedBinding);
completeButtonDisabled.bind(isNotExecutingBinding);
cancelButtonDisabled.bind(isNotExecutingBinding);
But this does not work and I do not understand why. It seems that the property binding for modelProperty() does not work here! Can you explain to me how-to convert the first working solution (at least without null) to a proper null-safe solution?
Edit 2016-04-26: The suggested solution does not work, therefore I created a simple fully-working example:
Mission.java:
package de.florianwolters.example.javafx.bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Mission {
enum Status {
CREATED,
EXECUTING,
COMPLETED,
CANCELED;
}
private final StringProperty shortName = new SimpleStringProperty();
private final ObjectProperty<Status> status = new SimpleObjectProperty<>();
public Mission(final String shortName) {
this.setShortName(shortName);
this.setStatus(Status.CREATED);
}
public String getShortName() {
return shortNameProperty().get();
}
public void setShortName(final String shortName) {
shortNameProperty().set(shortName);
}
public StringProperty shortNameProperty() {
return shortName;
}
public Status getStatus() {
return statusProperty().get();
}
public void setStatus(final Status status) {
statusProperty().set(status);
}
public ObjectProperty<Status> statusProperty() {
return status;
}
}
MissionDetailsViewModel.java:
package de.florianwolters.example.javafx.bindings;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleObjectProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class MissionDetailsViewModel {
/**
* The logger used for logging in the `MissionDetailsViewModel` class.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(
MissionDetailsViewModel.class);
private ObjectProperty<Mission> mission = new SimpleObjectProperty<>();
private final ReadOnlyBooleanWrapper executeButtonDisabled = new ReadOnlyBooleanWrapper(true);
private final ReadOnlyBooleanWrapper completeButtonDisabled = new ReadOnlyBooleanWrapper(true);
private final ReadOnlyBooleanWrapper cancelButtonDisabled = new ReadOnlyBooleanWrapper(true);
/**
* Constructs a `MissionDetailsViewModel`.
*/
public MissionDetailsViewModel(final ObjectProperty<Mission> mission) {
this.mission.bind(mission);
// partialWorkingBinding();
notWorkingBinding();
}
private void notWorkingBinding() {
final BooleanBinding isNotCreatedBinding = Bindings.createBooleanBinding(
() -> missionProperty().isNull().get()
? true
: missionProperty().get().statusProperty().isNotEqualTo(Mission.Status.CREATED).get(),
missionProperty());
final BooleanBinding isNotExecutingBinding = Bindings.createBooleanBinding(
() -> mission.isNull().get()
? true
: missionProperty().get().statusProperty().isNotEqualTo(Mission.Status.EXECUTING).get(),
missionProperty());
executeButtonDisabled.bind(isNotCreatedBinding);
completeButtonDisabled.bind(isNotExecutingBinding);
cancelButtonDisabled.bind(isNotExecutingBinding);
}
private void partialWorkingBinding() {
executeButtonDisabled.bind(missionProperty().isNotNull().and(missionProperty().get().statusProperty().isNotEqualTo(Mission.Status.CREATED)));
final BooleanBinding isNotExecutingBinding = missionProperty().isNotNull().and(missionProperty().get().statusProperty().isNotEqualTo(Mission.Status.EXECUTING));
completeButtonDisabled.bind(isNotExecutingBinding);
cancelButtonDisabled.bind(isNotExecutingBinding);
}
public boolean isExecuteButtonDisabled() {
return executeButtonDisabledProperty().get();
}
public ReadOnlyBooleanProperty executeButtonDisabledProperty() {
return executeButtonDisabled;
}
public boolean isCompleteButtonDisabled() {
return completeButtonDisabledProperty().get();
}
public ReadOnlyBooleanProperty completeButtonDisabledProperty() {
return completeButtonDisabled;
}
public boolean isCancelButtonDisabled() {
return cancelButtonDisabledProperty().get();
}
public ReadOnlyBooleanProperty cancelButtonDisabledProperty() {
return cancelButtonDisabled;
}
public Mission getMission() {
return missionProperty().get();
}
public void setMission(final Mission mission) {
missionProperty().set(mission);
}
public ObjectProperty<Mission> missionProperty() {
return mission;
}
}
MissionDetailsViewModelTest.java:
package de.florianwolters.example.javafx.bindings;
import static eu.lestard.assertj.javafx.api.Assertions.assertThat;
import javafx.beans.property.SimpleObjectProperty;
import org.junit.Before;
import org.junit.Test;
public final class MissionDetailsViewModelTest {
private Mission mission;
private MissionDetailsViewModel viewModel;
#Before
public void setUp() {
mission = new Mission("My Short Name");
viewModel = new MissionDetailsViewModel(new SimpleObjectProperty<Mission>(mission));
}
#Test
public void testInitialValues() {
assertThat(viewModel.executeButtonDisabledProperty()).isFalse();
assertThat(viewModel.completeButtonDisabledProperty()).isTrue();
assertThat(viewModel.cancelButtonDisabledProperty()).isTrue();
}
#Test
public void testMissionStatusSetToExecuting() {
mission.setStatus(Mission.Status.EXECUTING);
assertThat(viewModel.executeButtonDisabledProperty()).isTrue();
assertThat(viewModel.completeButtonDisabledProperty()).isFalse();
assertThat(viewModel.cancelButtonDisabledProperty()).isFalse();
}
#Test
public void testMissionStatusSetToCompleted() {
mission.setStatus(Mission.Status.COMPLETED);
assertThat(viewModel.executeButtonDisabledProperty()).isTrue();
assertThat(viewModel.completeButtonDisabledProperty()).isTrue();
assertThat(viewModel.cancelButtonDisabledProperty()).isTrue();
}
#Test
public void testMissionStatusSetToCanceled() {
mission.setStatus(Mission.Status.CANCELED);
assertThat(viewModel.executeButtonDisabledProperty()).isTrue();
assertThat(viewModel.completeButtonDisabledProperty()).isTrue();
assertThat(viewModel.cancelButtonDisabledProperty()).isTrue();
}
}
The unit test fails with the code above (the method notWorkingBinding() is used) but works with the method partialWorkingBinding(). What am I doing wrong?
You set up the calculation function for isNotCreatedBinding, but you didn't set the dependencies for the binding. You need to add mision as dependency:
Bindings.createBooleanBinding(
() -> mission.isNull().getValue()
? true
: missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.CREATED).getValue(), mission);
EDIT
You need to listen to the statusProperty instead of missionProperty, which will not work with createBooleanBinding when missionProperty().get() == null.
But you can use a When binding:
(is causing a NullPointerException as already mentioned in the question)
BooleanBinding isNotCreatedBinding = new When(mission.isNotNull()).then(mission.get().statusProperty().isNotEqualTo(Mission.Status.CREATED)).otherwise(false);
Or a more low-level solution:
missionProperty().addListener((ov, m, m1) -> {
if (m1 != null) {
executeButtonDisabled.bind(m1.statusProperty().isNotEqualTo(Mission.Status.CREATED));
}else {
executeButtonDisabled.unbind();
executeButtonDisabled.set(false);
}
});
Tomas Mikula's ReactFX framework (v 2.0) has this functionality built in:
import org.reactfx.value.Val;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
public class NestedBindingTest {
public static void main(String[] args) {
BooleanProperty disable = new SimpleBooleanProperty();
disable.addListener((obs, wasDisabled, isNowDisabled) ->
System.out.println("disable: "+wasDisabled+" -> "+isNowDisabled));
ObjectProperty<Item> item = new SimpleObjectProperty<>();
Val<Item.Status> status = Val.flatMap(item, Item::statusProperty);
disable.bind(status.map(s -> s == Item.Status.PENDING).orElseConst(true));
Item i = new Item();
System.out.println("Setting item");
item.set(i);
System.out.println("Setting item status to PENDING");
i.setStatus(Item.Status.PENDING);
System.out.println("Setting item status to READY");
i.setStatus(Item.Status.READY);
System.out.println("Setting item to null");
item.set(null);
}
public static class Item {
public enum Status {PENDING, READY}
private final ObjectProperty<Status> status = new SimpleObjectProperty<>();
public final ObjectProperty<Status> statusProperty() {
return this.status;
}
public final NestedBindingTest.Item.Status getStatus() {
return this.statusProperty().get();
}
public final void setStatus(final NestedBindingTest.Item.Status status) {
this.statusProperty().set(status);
}
}
}
Need to setup JMock code to test call back with google protobuf
Full project is located at http://github.com/andrewmilkowski/template-transport
In short, following are methods signatures (below)
what I need to do is to test method getLongValue, using Jmock JUnit4Mockery
what is the best and cleanest way to go about this
thanks much!
package com.argmaps.client.proto;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.fepss.rpc.server.RpcApplication;
import com.fepss.rpc.client.RpcChannelImpl;
import org.apache.tapestry5.ioc.MappedConfiguration;
import com.google.protobuf.RpcController;
import com.google.protobuf.RpcCallback;
import com.argmaps.transport.proto.generated.TransportServer.ProtoService;
import com.argmaps.transport.proto.generated.TransportServer.ProtoService.Stub;
import com.argmaps.transport.proto.generated.TransportServer.DefaultLongValue;
import com.argmaps.transport.proto.generated.TransportServer.LongValue;
import com.argmaps.transport.proto.fepss.ProtoServer.TransportHandler;
public class TransportClient {
protected final Log LOG = LogFactory.getLog(this.getClass().getName());
private RpcController controller;
private TransportHandler transportHandler;
private ProtoService.Interface service;
private void open(String host, int port) {
RpcChannelImpl channel = new RpcChannelImpl(host, port);
controller = channel.newRpcController();
service = ProtoService.newStub(channel);
}
protected static class LongValueRpcCallback implements RpcCallback<LongValue> {
private long longValue = 0L;
#Override
public void run(LongValue result) {
longValue = result.getLongValue();
}
private long getLongValue() {
return longValue;
}
}
private void close() {
}
public long getLongValue(LongValueRpcCallback longValueRpcCallback) {
DefaultLongValue defaultLongValue = DefaultLongValue.newBuilder().setLongValue(0L).build();
service.getLongValue(controller, defaultLongValue, longValueRpcCallback);
if (LOG.isDebugEnabled()) {
LOG.debug("Long value from server:" + longValueRpcCallback.getLongValue());
}
return longValueRpcCallback.getLongValue();
}
public static void main(String[] args) {
String host = "localhost";
int port = 9090;
final String portArgKey = "--port=";
for (String cmd : args) {
if (cmd.startsWith(portArgKey)) {
port = Integer.parseInt(cmd.substring(portArgKey.length()));
break;
}
}
TransportClient c = new TransportClient();
c.open(host, port);
c.getLongValue(new LongValueRpcCallback());
c.close();
}
public TransportClient() {
}
public static class TransportModule {
public static void contributeIoHandler(MappedConfiguration<String, ProtoService> configruation) {
configruation.add(ProtoService.getDescriptor().getFullName(), new TransportHandler());
}
}
}
Because of the callback, needed to:
create abstract class LongValueRpcCallbackTemplate implements RpcCallback
create class LongValueRpcCallback extends LongValueRpcCallbackTemplate
and then complete implementation in the test class
Test class:
package com.argmaps.client.proto;
import com.argmaps.transport.proto.generated.TransportServer;
import com.fepss.rpc.client.RpcChannelImpl;
import com.google.protobuf.RpcController;
import org.jmock.Expectations;
import org.junit.Test;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import static org.junit.Assert.assertEquals;
public class TransportClientTest {
Mockery context;
#Before
public void before() {
context = new JUnit4Mockery();
}
private class TestLongValueRpcCallback extends LongValueRpcCallbackTemplate {
private long longValue = 123456789L;
#Override
protected long getLongValue() {
return longValue;
}
}
#Test
public void testGetLongValue() {
final TransportServer.ProtoService.Interface mockedTransportServer = context.mock(TransportServer.ProtoService.Interface.class);
final RpcChannelImpl channel = new RpcChannelImpl("localhost", 9090);
final RpcController controller = channel.newRpcController();
final TransportServer.DefaultLongValue defaultLongValue = TransportServer.DefaultLongValue.newBuilder().setLongValue(0L).build();
com.argmaps.client.proto.TransportClient testObject = new TransportClient(controller, mockedTransportServer);
final TestLongValueRpcCallback testLongValueRpcCallback = new TestLongValueRpcCallback();
final long testLongValue = 123456789L;
context.checking(new Expectations() {
{
one(mockedTransportServer).getLongValue(controller, defaultLongValue, testLongValueRpcCallback);
}
});
assertEquals(testLongValue, testObject.getLongValue(testLongValueRpcCallback));
}
}