Tag Archives: Spring

JavaFX: Instantiate Application within Spring Context

JavaFX application is typically instantiated via calling static method launch(…) on object inherited from Application:

import javafx.application.Application;

public class TestApp extends Application {
	public static void main(String[] args) {
		launch(args);
	}
}

But this hardly fits into IoC solution – in this case Spring Context. I was trying to find suitable solutions but failed. There is no simple way how to instantiate Application in dynamic way.

But there is workaround: Start application in static way, in start(…) method save created Application instance into static variable and then create Spring context. Instance of Application is accessible through static method.

So something like:

import org.apache.commons.lang3.Validate;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TestApp extends Application {
	private static volatile TestApp singleton;
	
	public static void main(String[] args) {
		launch(args);
	}

	public static TestApp getSingleton() {
		Validate.notNull(singleton, "Not yet created instance of %s!", TestApp.class.getName());
		return singleton;
	}

	@Override
	public void start(Stage primaryStage) throws Exception {
		Validate.validState(singleton==null, "Only one instance of %s may be created!", getClass().getName());
		
		singleton = this;
		
		try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/beans.xml")) {
			// Context initialized!!!
		}
	}
}

With following in beans.xml:

<bean id="app" class="test.TestApp" factory-method="getSingleton"/>

Then “app” behaves as plain bean and you can use it as such.

Gradle: Spring + FatJar = dependency on Internet connection

See my recent blog post for FatJar configuration sample. Today I found nasty behavior of Gradle plugin FatJar when used on Spring project. I hope that this post will help you not to spend whole day with it – as I did.

If your project uses only one version of each library, java packages system should assure that there will be no conflicts when merging multiple jar libraries into one big. But that does not apply to directory META-INF. This directory typically does not use packages system, so conflicts may appear.

FatJar plugin handles this situation in simple way – it just copies each file in multiple versions:

mess

That – of course – causes issues when loading. Typically only one file is picked. For example Spring stores in spring.schemas paths in resources where XSD schema files referred from your beans.xml are located in resources (so it does not need to load it from Internet). But do that for each module – Context, AOP, Web, … So only one module works.

This problem does not appear on machine with available Internet connection, because it just downloads schemas from addresses like http://www.springframework.org/schema/beans/spring-beans-3.0.xsd. But when you deploy your app on intranet server, problem appears:


143306.358000 2016-01-26 [ main] [ WARN] [factory.xml.XmlBeanDefinitionReader] [] : Ignored XML validation warning
org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document 'http://www.springframework.org/schema/beans/spring-beans-3.0.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not &amp;lt;xsd:schema&amp;gt;.
at

....
Caused by: java.net.UnknownHostException: www.springframework.org
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184) ~[?:1.8.0_72-internal]
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[?:1.8.0_72-internal]
at java.net.Socket.connect(Socket.java:589) ~[?:1.8.0_72-internal]
at java.net.Socket.connect(Socket.java:538) ~[?:1.8.0_72-internal]

And then:

Exception in thread &quot;main&quot; org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 6 in XML document from class path resource [grab_mode_beans.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 6; columnNumber: 69; cvc-elt.1: Cannot find the declaration of element 'beans'.
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:399)
....
Caused by: org.xml.sax.SAXParseException; lineNumber: 6; columnNumber: 69; cvc-elt.1: Cannot find the declaration of element 'beans'.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)

Solution is to use shadowJar. It may look like this:

buildscript {
  repositories {
    maven {
      url "https://maven.eveoh.nl/content/repositories/releases"
    }

    mavenCentral()
      jcenter()
    }

  dependencies {
    classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.2'
  }
}

apply plugin: 'com.github.johnrengelman.shadow'

dependencies {
  compile project(":MyOwnProjectExample")
}

shadowJar {
archiveName "MyTest.jar";

append("META-INF/spring.schemas")
append("META-INF/spring.handlers")

manifest {
  attributes("Main-Class": "com.my.Test" )
  }
}

//this helps if you like to build fatJar as part of normal build
//otherwise use "gradle shadowJar";
assemble.dependsOn shadowJar
build.dependsOn shadowJar

I already suffered at least by 3 similar issues with following libraries:
* Spring
* Log4j2
* Lucene

I hope it helps!

Add dynamic properties into Spring using annotations

I am working on command line application and I wanted to pass parameters from command line (read using Commons CLI) to member variables using annotation @Value.

  1. Put your properties into standard Java Properties
  2. Do not pass parameter to AnnotationConfigApplicationContext constructor, but register your configuration manually
  3. Add your properties wrapped by PropertiesPropertySource into ConfigurableEnvironment
  4. Create bean of class PropertySourcesPlaceholderConfigurer (This is crucial!!!). Without this step it was possible to lookup my properties using env.resolvePlaceholder(…) but not using annotation @Value.

package my.test;

import java.util.Properties;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.PropertiesPropertySource;

@Configuration
@ComponentScan(value = {
 "my.test.components"
})
public class DIConfiguration {

public static AnnotationConfigApplicationContext createContext(Properties properties) {
 PropertiesPropertySource propertiesSource = new PropertiesPropertySource("test", properties);

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
 context.getEnvironment().getPropertySources().addLast(propertiesSource);
 context.register(DIConfiguration.class);
 context.refresh();

return context;
 }

public DIConfiguration() {
 }

@Bean
 public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
 return new PropertySourcesPlaceholderConfigurer();
 }
}

Then simply annotate your fields:


package my.test.components;

import java.io.File;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class EmulatorConfig {

@Value("${test.testFile}")
 private File testFile;

}

And use it:

Properties properties = new Properties();

//in real case fill properties by command line parameters
properties.put("test.testFile", new File("test.txt"));
properties.put("test.testInt", 15);
properties.put("test.testBool", true);

AnnotationConfigApplicationContext context = DIConfiguration.createContext(properties);

....