Different keys for Debug and Release Build

In lots of projects i had the problem, that i need different key for a live environment and the environment during the development. for example currently i use parse.com for my cloud backend. there is a live instance (with application_id and client_id) and a development instance. until now all the time i had to switch the instances by copy the right key in the string.xml file. in this post u can see my mechanism for that by using gradle.

the following code is for initialize the parse framework in my android project

Parse.initialize(this, getString(R.string.parse_application_id), getString(R.string.parse_client_key));

the keys for the application_id and the client_key are in the string.xml file. at the beginning they are empty. looks strange but later this will make sense ;)

<?xml version="1.0" encoding="utf-8"?>
<resources>

 <string name="parse_application_id"></string>
 <string name="parse_client_key"></string>

</resources>

the following code is my gradle.build file for my android application

apply plugin: 'android'

android {
 ...
 defaultConfig {
 ...
 }
 signingConfigs {
 ...
 }
 buildTypes {
 release {
 ...

 }
 debug {
 ...
 }
 }
}
dependencies {
 ...
}

android.applicationVariants.all { variant ->
   variant.mergeResources.dependsOn {
      overrideParseKey(variant)
   }
}

def overrideParseKey(def buildVariant) {
 def xmlStringsFileAsNode = new XmlParser().parse("app/src/main/res/values/strings.xml")

 xmlStringsFileAsNode.each {

    // debug
    if (buildVariant.name.equals("debug")) {
       if (it.attribute("name").equals("parse_application_id")) {
          it.setValue("DEBUG_PARSE_APPLICATION_ID")
       }
       if (it.attribute("name").equals("parse_client_key")) {
          it.setValue("DEBUG_PARSE_CLIENT_ID")
       }
    }

    // release
    if (buildVariant.name.equals("release")) {
       if (it.attribute("name").equals("parse_application_id")) {
          it.setValue("RELEASE_PARSE_APPLICATION_ID")
       }
       if (it.attribute("name").equals("parse_client_key")) {
          it.setValue("RELEASE_PARSE_CLIENT_ID")
       }
    }

    def fileWriter = new FileWriter("app/src/main/res/values/strings.xml")
    def printWriter = new PrintWriter(fileWriter)
    printWriter.print("""<?xml version="1.0" encoding="utf-8"?>\n""")
    def xmlPrinter = new XmlNodePrinter(printWriter)
    xmlPrinter.setPreserveWhitespace(true)
    xmlPrinter.print(xmlStringsFileAsNode)
 }
 createOverrideDirIfNecesarry()
}

def createOverrideDirIfNecesarry() {
   def file = new File("app/src/main/res/values/")
   if (!file.exists()) {
      file.mkdirs()
   }
}

so what happen in this gradle script? at first u can see 2 build types (release and debug). i build the release apk by using gradle assembleRelease with the signingConfigs. the debug type is used by me with gradle assembleDebug and the debug type is also used by the android studio by default!

if u rebuild your project (android studio will do this also automatically) –> build –> rebuild project, the gradle script will be executed. the script will replace the empty entries in the string.xml file withe the debug keys!!! is that cool? yes it is. and if you build the application with gradle assembleRelease the script will replace the entries with the release keys!

Swing2JavaFX Live Coding Example (WJAX)

Last week I did some Swing2JavaFX Live Coding on the WJAX in Munich (Details). Therefore I migrated a random Swing client from GitHub partially to JavaFX. I created a fork and a screencast (German), which gives you an impression, which technical opportunities you have, if you migrate Swing to JavaFX.

I also pointed some hurdles and showed workarounds for existing problems if you migrate from Swing to JavaFX.

GitHub – Fork of the Swing client
https://github.com/sialcasa/Swing2JavaFXHacking

Screencast Swing2JavaFX Live Coding

 

How to test JavaFX Services

In this post we want to take a look on how to write an integration test for a JavaFX Service.  Our showcase is a Service that performs a login and signals whether the login was successful or not. Please have a look on the code below to understand how the login works. As I said, this is an integration test for the combination of the Service and the Task in it. In addition you should write an Unit Test for the task in the Service .

public class LoginService extends Service {

    private final String password;
    private final String username;

    private ClientApi clientApi;

    @Inject
    public LoginService(Client Api, String username, String password) {
        this.username = username;
        this.password = password;
        this.clientApi = clientApi;
    }

    @Override
    protected Task createTask() {
        Task task = new Task() {
            @Override
            protected Boolean call() throws Exception {
                 // true/false for login state, Exception for Networkproblems
                 Boolean login;
                 try {
                      login = clientApi.login(username, password);
                 } catch (Exception e) {
                      //If error occures, set fail message and throw e to make the task fail
                      updateMessage("Error while logging in");
                      throw e;
                 }
                 if (!login) {
                      updateMessage("Invalid credentials");
                 }
                 return login;
            }
        };

        return task;
    }
}

As you can see, the service follows the Dependency Injection pattern, so we can mock the dependencies easily.


What do we want to test?

 

  • Successful login
    • stateProperty() is Worker.State.SUCCEEDED
    • valueProperty() is true
    • messageProperty() is “”

 

  • Failed login because of wrong credentials
    • stateProperty() is Worker.State.SUCCEEDED
    • valueProperty() is true
    • messageProperty() is “Invalid Credentials”

 

  • Failed login because of  network errors
    • stateProperty() is Worker.State.FAILED
    • valueProperty() is false
    • messageProperty() is “Error while logging in” 

 


Problems which occurs when you test a Service

  1. A service needs an initialized JavaFX Toolkit
  2. A services executes the tasks in separate thread which makes it hard to perform asserts

 

Solution for Problem 1

As I already mentioned we need to initialize the JavaFX Toolkit, otherwise we will get this Exception when we start the Service:

java.lang.IllegalStateException: Toolkit not initialized
	at com.sun.javafx.application.PlatformImpl.runLater(Unknown Source)
	at com.sun.javafx.application.PlatformImpl.runLater(Unknown Source)
	at javafx.application.Platform.runLater(Unknown Source)
	at javafx.concurrent.Service.runLater(Unknown Source)
	at javafx.concurrent.Service.start(Unknown Source)
	at de.saxsys.javafx.test.example.LoginServiceTest.failedLoginWithNetworkErrors(LoginServiceTest.java:79)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

I created a small JUnit TestRunner to solve this problem. The Runner manages the JavaFX Toolkit initialization. Please read the following post for more information:

http://blog.buildpath.de/javafx-testrunner/

We’ll see how to use the Runner in our following code boxes.

Solution for Problem 2

To handle the new thread which is spawned by the Service we use a CompletableFuture<T> that blocks the Test-Thread until the complete method of the future was called.

In the example you can see a CompletableFutures that will complete when the state of the Serviceis the state Worker.State.DONE.

If this happens, we transport the current values of the Service with a DTO through the CompletableFuture back to our test thread.

CompletableFuture<ServiceTestResults> serviceStateDoneFuture = new CompletableFuture<>();

service.stateProperty().addListener((observable, oldValue, newValue) -> {
     //We check, whether the new state is the state we are looking for to make our tests
     if (newValue == Worker.State.Done) {
            serviceStateReady.complete(
             new ServiceTestResults(service.getMessage(), service.getState(),service.getValue())
            );
     }
});

service.start();

// This call is blocking the thread until complete() was called and get() gives an result. You can set a timeout 
ServiceTestResults result = serviceStateDoneFuture.get(100, TimeUnit.MILLISECONDS); 

Assert.assertEquals("YEA WE CAN ASSERT",result.message)
// DTO to transport the results of the check
private class ServiceTestResults {
   
  String message;
  Worker.State state;
  Boolean serviceResult;

  ServiceTestResults(String message, State state, Boolean serviceResult) {
      this.message = message;
      this.state = state;
      this.serviceResult = serviceResult;
  }
}

Test the LoginService

Ok, now we can write the tests for our LoginService. With the combination of the Test Runner and CompletableFuture we can test all cases that we wanted. I hope the code explains itself – If you have further questions please comment under the post.

package de.saxsys.javafx.test.example;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

import de.saxsys.javafx.test.JfxRunner;

//The JfxRunner ensures, that an JavaFX-Application is present
@RunWith(JfxRunner.class)
public class LoginServiceTest {

    private static final String EMPTY_STRING = "";

    @Test
    public void testStateBySuccessFulLogin() throws Exception {
        // Mock the CientAPI to have a successful login
        ClientApi mockedClientApi = (username, password) -> true;
        // Create the Service with the mocked ClientAPI
        LoginService service = new LoginService(mockedClientApi, EMPTY_STRING, EMPTY_STRING);

        // Create a completableFuture to test the background task of the
        // service
        CompletableFuture<ServiceTestResults> stateCompareableFuture = createStateCompareFuture(service,
                Worker.State.SUCCEEDED);

        // We are not in the UI Thread so we have to start the service in
        // runLater()
        service.start();

        // get the result of the CompareableFuture with a timeout when the
        // result does not appear
        ServiceTestResults result = stateCompareableFuture.get(100, TimeUnit.MILLISECONDS);

        // Tests
        Assert.assertEquals(Worker.State.SUCCEEDED, result.state);
        Assert.assertEquals(EMPTY_STRING, result.message);
        Assert.assertTrue(result.serviceResult);
    }

    @Test
    public void failedLoginWithWrongCredentials() throws Exception {
        // Mock the CientAPI to have a failed login because of invalid
        // credentials
        ClientApi mockedClientApi = (username, password) -> false;
        // Create the Service with the mocked ClientAPI
        LoginService service = new LoginService(mockedClientApi, EMPTY_STRING, EMPTY_STRING);
        // Create a completableFuture to test the background task of the
        // service
        CompletableFuture<ServiceTestResults> stateCompareFuture = createStateCompareFuture(service,
                Worker.State.SUCCEEDED);

        service.start();

        // get the result of the CompareableFuture with a timeout when the
        // result does not appear
        ServiceTestResults result = stateCompareFuture.get(100, TimeUnit.MILLISECONDS);

        // Tests
        Assert.assertEquals(Worker.State.SUCCEEDED, result.state);
        Assert.assertEquals("Invalid credentials", result.message);
        Assert.assertFalse(result.serviceResult);
    }

    @Test
    public void failedLoginWithNetworkErrors() throws Exception {
        // Mock the CientAPI to have a failed login with some errors
        ClientApi mockedClientApi = (username, password) -> {
            throw new RuntimeException();
        };
        // Create the Service with the mocked ClientAPI
        LoginService service = new LoginService(mockedClientApi, EMPTY_STRING, EMPTY_STRING);

        // Create a complteableFuture to test the background task of the
        // service
        CompletableFuture<ServiceTestResults> stateCompareFuture = createStateCompareFuture(service,
                Worker.State.FAILED);

        service.start();

        // get the result of the CompareableFuture with a timeout when the
        // result does not appear
        ServiceTestResults result = stateCompareFuture.get(100, TimeUnit.MILLISECONDS);

        // Tests
        Assert.assertEquals(Worker.State.FAILED, result.state);
        Assert.assertEquals("Error while logging in", result.message);
        Assert.assertNull(result.serviceResult);
    }

    // Helper method which creates a compareableFuture that tells us, when the
    // state of the service reached a state which we expect
    private CompletableFuture<ServiceTestResults> createStateCompareFuture(LoginService service,
            Worker.State stateToReach) {
        CompletableFuture<ServiceTestResults> serviceStateReady = new CompletableFuture<>();
        service.stateProperty().addListener(
                (observable, oldValue, newValue) -> {
                    if (newValue == stateToReach) {
                        serviceStateReady.complete(new ServiceTestResults(service.getMessage(), service.getState(),
                                service.getValue()));
                    }
                });
        return serviceStateReady;
    }

    // DTO to transport the results of the check
    private class ServiceTestResults {
        String message;
        Worker.State state;
        Boolean serviceResult;

        ServiceTestResults(String message, State state, Boolean serviceResult) {
            this.message = message;
            this.state = state;
            this.serviceResult = serviceResult;
        }
    }
}

 

JavaFX Testrunner

 

I deployed a small JUnit TestRunner based on http://awhite.blogspot.de/2013/04/javafx-junit-testing.html into the maven central repository.

Project on GitHub

You can use it by adding the following dependency to your project:

<dependency>
        <groupId>de.saxsys</groupId>
        <artifactId>jfx-testrunner</artifactId>
        <version>1.0</version>
</dependency>

Some JavaFX components does need the JavaFX-Application to be initialized. The TestRunner ensures that you don’t have to mind about an existing JavaFX Application for your Unit Tests. In addition you can choose which test method should be performed in the UI-Thread  by using the Annotation @TestInJfxThread.

 

@RunWith(JfxRunner.class)
public class TestClass {

    @Test
    public void testWithoutFXThread() throws Exception {
        Assert.assertFalse(Platform.isFxApplicationThread());

        CompletableFuture isInApplicationThread = new CompletableFuture<>();
        
        Platform.runLater(() -> {
            isInApplicationThread.complete(Platform.isFxApplicationThread());
        });
        
        Assert.assertTrue(isInApplicationThread.get());
    }

    @Test
    @TestInJfxThread
    public void testWithFXThread() throws Exception {
        Assert.assertTrue(Platform.isFxApplicationThread());
    }
}

JavaFX – Decouple the View and its behavior to create a testable UI

The behavior of an UI is a complex state machine which should be tested. This can be done not only by automated UI-tests, but also with Unit Tests. It’s not easy to write Unit Tests for UIs,  because you need to decouple the state of the UI from its declaration.

One approach is the MVVM pattern. MVVM introduces a ViewModel layer that contains the state and behavior of an UI. The View only represents the ViewModel and reacts on its changes.

Shortfacts MVVM:

  • View does not know the Model
  • Model does not know anybody ;-)
  • ViewModel does not know the View (one difference to MVP)
  • ViewModel knows Model

 

Let’s try it. We want to build a login component which has two text fields (name/password) and a login button. The button should only be active when there is some text in the text fields. You can find the example on GitHub.

Step 1 - Write tests to specify the ViewModel

Because the ViewModel represents the UI state, we create the ViewModel first.Testdriven. Therefore we write an unit test to specify the behavior. The ViewModel should provide three properties which can be accessed

  • userNameProperty() – represents the current user input for the username
  • passwordProperty() - represents the current user input for the password
  • loginPossibleProperty() - represents whether a login is possible based on conditions (username and password not empty)

 

public class LoginViewModelTest {
	@Test
	public void disableLoginButton() throws Exception {
		LoginViewModel loginViewModel = new LoginViewModel();
		// No username and password were set
		Assert.assertFalse(loginViewModel.isLoginPossibleProperty().get());

		loginViewModel.userNameProperty().set("Stefanie Albrecht");
		// username was set, but no password
		Assert.assertFalse(loginViewModel.isLoginPossibleProperty().get());

		loginViewModel.passwordProperty().set("<3");
		// username and password were set
		Assert.assertTrue(loginViewModel.isLoginPossibleProperty().get());
	}
}

Step 2 - Implement the ViewModel, so the tests can pass

Yea – we have a test, but the test won’t compile and pass, if we don’t provide a proper implementation. So lets implement the ViewModel.

The userName and the password properties are representing the current input of upcoming text fields, so they are of the type StringProperty. The loginPossible property signals whether the user provided some input for the username and password field. It should be true, if both other properties have values.

public class LoginViewModel {

	private final StringProperty userName = new SimpleStringProperty();
	private final StringProperty password = new SimpleStringProperty();
	private final ReadOnlyBooleanWrapper loginPossible = new ReadOnlyBooleanWrapper();

	public LoginViewModel() {
                //Logic to check, whether the login is possible or not
		loginPossible.bind(userName.isNotEmpty().and(password.isNotEmpty()));
	}

	public StringProperty userNameProperty() {
		return userName;
	}

	public StringProperty passwordProperty() {
		return password;
	}

	public ReadOnlyBooleanProperty isLoginPossibleProperty() {
		return loginPossible.getReadOnlyProperty();
	}
}

Step 3 - Implement the View and bind it to the ViewModel

We can implement the View after the unit test for the ViewModel passed. I suggest to create a FXML-file by using the Scene Builder first. Now we create the JavaFX-Controller which is also part of the View layer in MVVM. You can view both files in the next code box by clicking on the tabs of the box. Let’s connect the View and the ViewModel by data binding, which ensures the propagation of changes in the properties. As we want the login button to be deactivated when there is no input in the text fields, we have to bind the disableProperty() of the login button to the isLoginPossibleProperty() in the ViewModel.

Login with Scene Builder

public class LoginView {

	@FXML
	private TextField userNameTextField;

	@FXML
	private PasswordField passwordTextField;

	@FXML
	private Button loginButton;

	@FXML
	void initialize() {
                //Create the ViewModel - In production this should be done by dependency injection
		LoginViewModel loginViewModel = new LoginViewModel();
                //Connect the ViewModel
		userNameTextField.textProperty().bindBidirectional(loginViewModel.userNameProperty());
		passwordTextField.textProperty().bindBidirectional(loginViewModel.passwordProperty());
		loginButton.disableProperty().bind(loginViewModel.isLoginPossibleProperty().not());
	}
}
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.saxsys.mvvm.LoginView">
 <children>
  <VBox alignment="CENTER_RIGHT" spacing="10.0">
   <children>
    <TextField fx:id="userNameTextField" alignment="BOTTOM_RIGHT" promptText="Username" />
    <PasswordField fx:id="passwordTextField" alignment="CENTER_RIGHT" promptText="Password" />
    <Button fx:id="loginButton" mnemonicParsing="false" onAction="#onLoginButtonPressed" text="Login" textAlignment="CENTER" />
   </children>
   <padding>
    <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
   </padding>
  </VBox>
 </children>
 <opaqueInsets>
 <Insets />
 </opaqueInsets>
</AnchorPane>

If the user enters a username or password the changes will be propagated by data binding from the View to the ViewModel. The ViewModel reevaluates the defined binding of the isLoginPossibleProperty(). A possible change of the isLoginPossibleProperty()  will be propagated to the disabledProperty() of the loginButton .

As you can see, we removed every logic from the view. The view is stupid and the behavior testable.

The example can be found on GitHub.

create android dependency with gradle and upload artifact to maven repository

some weeks ago i would like to share a set of util classes from my android project with a colleague. and whats better than create a jar and upload it into a maven repository? the following code show how to create a jar file and upload it to a maven repository by using gradle

apply plugin: 'android'
apply plugin: 'maven'

def computeVersionName() {
     return "0.1.0-SNAPSHOT"
}

android {
     // defaultConfig, buildTypes and stuff

     task AnimationUtilsLibJar(type: Jar) {
          archiveName = "animation-" + computeVersionName()
          from fileTree(dir: 'build/classes/debug')
     }
     artifacts {
          archives AnimationUtilsLibJar
     }
uploadArchives {
     repositories {
          mavenDeployer {
               pom.groupId = 'de.buildpath.utils.android'
               pom.artifactId = 'animation'
               pom.version = computeVersionName()

               Console console = System.console()
               def username = null;
               def password = null;

               if (console != null) {
                    username = new String(console.readLine("\nUsername: "))
                    password = new String(console.readPassword("Password: "))

                    if (computeVersionName().endsWith("-SNAPSHOT")) {
                         snapshotRepository(url: "SNAPSHOT_REPO_URL"){
                            authentication(userName: username, password: password)
                         }
                    } else {
                         repository(url: "RELEASE_REPO_URL") {
                              authentication(userName: username, password: password)
                         }
                    }

                }
            }
        }
    }
}

to upload the artifact you have to open a terminal, change to the root directory of your project and execute the following command

gradle uploadArchives

now you can integrate the dependency in your android project

dependencies {
    compile 'de.buildpath.utils.android:animation:0.1.0-SNAPSHOT'
}

 

Persist data with greenDAO

In my last project i used the framework greenDao (http://greendao-orm.com/) for persisting data. this post will show you how to use it.

1. At first you have to create a simple java project to define and generating the entities

2. include DaoGenerator.jar to your buildpath

3. create a starter class like this

public class Start {
	private static final int SCHEMA_VERSION = 1;

        public static void main(final String[] args) throws Exception {

                final Schema schema = new Schema(SCHEMA_VERSION, "<MODEL_NAMESPACE>");

		new DaoGenerator().generateAll(schema, "<OUTPUT_PATH>");
        }
}

4. now you can define your entity

final Entity entityArea = schema.addEntity("Area");
entityArea.addStringProperty("areaId").primaryKey();
entityArea.addStringProperty("country").notNull();
entityArea.addDateProperty("lastSync").notNull();
entityArea.addStringProperty("name").notNull();
entityArea.addStringProperty("subscribed").notNull();

5. after executing the main method the result looks like this

public class Area {

    private String areaId;
    private String country;
    private java.util.Date lastSync;
    private String name;
    private String subscribed;

    private transient DaoSession daoSession;
    private transient AreaDao myDao;

    // KEEP FIELDS - put your custom fields here
    // KEEP FIELDS END

    public Area() {
    }

    public Area(String areaId) {
        this.areaId = areaId;
    }

    public Area(String areaId, String country, java.util.Date lastSync, String name, String subscribed) {
        this.areaId = areaId;
        this.country = country;
        this.lastSync = lastSync;
        this.name = name;
        this.subscribed = subscribed;
    }

// some getter, setter and some other stuff

}

6. but thats not all! look at the AreaDao class. inside of this class is all the sql stuff i dont want to write by my own. and the class extends from AbstractDao. so you have all the CRUD operations for free! awesome :-)

7. now you can use this classes in your android-project and you have do add the greendao.jar to your android project

8. in the next snipped i show you how easy it is to save an entity in your android project

public class DataProvider {
      private static final String DB_NAME = "my-db";
      private final SQLiteDatabase db;
      private AreaDao areaDao;

      public DataProvider(final Context context) {
            SQLiteDatabase.CursorFactory cursorFactory = null;
             final DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(context, DB_NAME, cursorFactory);

            db = helper.getWritableDatabase();
            DaoMaster daoMaster = new DaoMaster(db);
            daoSession = daoMaster.newSession();

            areaDao = daoSession.getAreaDao();
      }

      public void createArea(final Area area) {
            areaDao.insert(area);
      }
}

the setup looks a little bit like overhead. but remember – you get CRUD for free!

9. the last think i want to show you is the query api. here is a short example how you can use ist

areaDao.queryBuilder().where(AreaDao.Properties.Country.like("%island%")).list();
// that means give me all areas from countries that contains "island"

the query api has also methods like equals, greater than, between and all the other stuff we know from sql like ordering…

in addition it is possible to create relations between entities. for more infomation click here

FXML composition – how to get the controller of an included FXML?

The possibility to declare the UI with an FXML in JavaFX is pretty cool. In bigger applications you have to split the views in different FXMLs which allows you to reuse and compose them in complex FXMLs. Therefore you have to use the  <fx:include>  statement.

The include enables you to get the reference of the embedded view element in your FXML-controller class. Be sure that you use the value of fx:id as the name of the member in the controller class. The FXMLLoader will pass the reference once the View was created.

[...]
<fx:include fx:id="embeddedView" source="SomeOtherView.fxml"/>
[...]
class AViewController{
    @FXML
    Parent embeddedView; //fx:id value as Variable name -> JavaFX injects the reference of the root element in the included FXML
}

Ok, we have the reference of the view element, but how can we get the reference of the embeddedView’s controller?  The solution is a naming convention which you can find here.


Short example (GitHub)

First Step: Create the FXML and the Java Controller which we want to embed:

 <AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="de.saxsys.fxml.RedView">
 <children>
 <Pane prefHeight="200.0" prefWidth="200.0">
 <children>
 <Button fx:id="button1" layoutX="68.0" layoutY="50.0" mnemonicParsing="false" text="Button" />
 <Button fx:id="button2" layoutX="68.0" layoutY="121.0" mnemonicParsing="false" text="Button" />
 </children>
 </Pane>
 </children>
</AnchorPane>
public class RedView {

    @FXML
    private Button button2;

    @FXML
    private Button button1;

    public void foo(String foo) {
       System.out.println(foo);
    }
}
redview

RedView.fxml

Second Step: Create the View which should embed the RedView.fxml. It’s not problem to do this using the Scene Builder. If you are using the developer preview of the Scene Builder 2, you may have problems to set an fx:id to the included elements – in this case add the fx:id attribute directly in the fxml code.

Add

Include FXML files using the SceneBuilder – Don’t forget to set the fx:id to the included element

 <VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.saxsys.fxml.MainView">
 <children>
 <!--Some element -->
 <Circle radius="100.0" stroke="BLACK" strokeType="INSIDE" />
 <!--our included FXML -->
 <fx:include fx:id="embeddedRedView" source="RedView.fxml" />
 <!--Some other element -->
 <Circle radius="100.0" stroke="BLACK" strokeType="INSIDE" />
 </children>
</VBox>
public class MainView {

	@FXML
	private Parent embeddedRedView; //embeddedElement

	@FXML
	private RedView embeddedRedViewController; // $embeddedElement+Controller

	public void initialize() {
		System.out.println(embeddedRedViewController);
		System.out.println(embeddedRedView);
                embeddedRedViewController.foo("It works"); //Console print "It works"
	}
}
Added

Embedded RedView.fxml into MainView.fxml using the Scene Builder

As you can see, we can get the reference of the nested controller by appending the word Controller in addition to the variable name of the embedded element.

public class Starter extends Application {

	@Override
	public void start(Stage stage) throws Exception {

		Parent parent = FXMLLoader.load(getClass().getResource("MainView.fxml"));
		Scene scene = new Scene(parent);
		stage.setScene(scene);
		stage.show();
	}

	public static void main(String[] args) {
		launch(args);
	}

}

Let’s try it – create an JavaFX application which loads the MainView.fxml and you’ll see the printed message on the console, which prooves that we accessed the reference to the FXML-controller of the embedded FXML element.

Example on GitHub

iOS 8 Beta 3 – CocoaPods issue workaround

Today I updated to Beta 3 of iOS and OSX. After that I got some compile errors:

  • Syntax changes
  • API changes
  • Strange error belonging cocoapods
 Error after update
/[PATH_TO_PROJECT]/all-product-headers.yaml:4:13: error: Could not find closing ]!
  'roots': [
            ^
fatal error: invalid virtual filesystem overlay file '/[PATH_TO_PROJECT]/all-product-headers.yaml'
1 error generated.

 

How to fix
  1. Navigate in Finder to /[PATH_TO_PROJECT]/all-product-headers.yaml
  2. Replace the content with the next code block
  3. Lock the file by the file information dialog (afterwards the file should look like the image)
  4. Ignore compile errors in pod projects

 

Locked File

Locked File

{
  'version': 0,
  'case-sensitive': 'false',
  'roots': [
  ]
}