I have started reading the Apache commons documentation but its very extensive so I am hoping someone can answer a simple question so I don't need to read all of it just to start using it for basic configuration. I am losing patience really quick with it - there is no "quick start chapter" and I don't need to know every detail before I decide if I want to use the library or not.
I want (what I think is a common use-case) a class with static methods that provides look up of properties.
E.g. in class Foo i can use
Settings.config.getString("paramter");
Where
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.DefaultConfigurationBuilder;
/**
* Settings configuration class
*/
public class Settings {
private static final DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder("config.xml");
public static final Configuration config= factory.getConfiguration();
}
The problem is that the factory method can throw an exception! So this code does not compile, a class cannot throw an exception either so I suspect that I need to do a lot more coding.
I suspect that there is a simple solution to this. But it surely cannot be calling
DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder("config.xml");
Configuration config= factory.getConfiguration();
In every class where I want to read configurations?
I have tried:
public class Settings {
public static final Configuration config;
static {
try {
DefaultConfigurationBuilder factory;
factory = new DefaultConfigurationBuilder("config.xml") ;
config = factory.getConfiguration();
}
catch (ConfigurationException e) {
// Deal with the exception
config=null;
System.exit(1);
}
}
}
But I get the compilation error:
error: variable config might already have been assigned
[javac] config=null;
You could put the code to initialize config in a static initializer block and deal with the exception there. For example:
public class Settings {
public static final Configuration config;
static {
Configuration c = null;
try {
DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder("config.xml");
c = factory.getConfiguration();
}
catch (SomeException e) {
// Deal with the exception
c = null;
}
config = c;
}
}
Related
I'm trying to use a custom Configuration for saxon HE 10.3.
The Configuration is not being used. Presumably the config needs to be registered or installed? But how?
Here's my code:
final Configuration config = new net.sf.saxon.Configuration();
/**/ config.setLocalizerFactory(new LocalizerFactory() {
public Numberer getNumberer(final String language, final String country) {
if (language.equals("de")) {
return Numberer_de.getInstance();
} else {
return null;
}
}
});
net.sf.saxon.Transform.main(new String[] {
"-s:source.xml",
"-xsl:stylesheet.xslt",
"-o:result.txt"
});
You really don't want to be running net.sf.saxon.Transform.main from a Java application: use either the s9api or JAXP transformation APIs. The net.sf.saxon.Transform.main interface is designed for use from the command line, and it can therefore only modify the configuration through command line switches. It also has drawbacks like shutting down the Java VM if the transformation fails.
There is a workaround, which is to use the -init option on the command line to trigger user-supplied initialisation code (which has access to the Configuration object), but that's only really digging yourself deeper into your hole. I'd recommend switching to the s9api API.
Documentation: https://saxonica.com/documentation/index.html#!using-xsl/embedding
If you want to change the configuration when running Saxon from the command line, as Michael said, there is the -init option to pass in the name of a class implementing the Initializer interface https://saxonica.com/documentation/index.html#!javadoc/net.sf.saxon.lib/Initializer so you would use roughly e.g.
package com.example;
import net.sf.saxon.option.local.Numberer_de;
import net.sf.saxon.lib.Initializer;
import net.sf.saxon.Configuration;
import net.sf.saxon.lib.LocalizerFactory;
import net.sf.saxon.lib.Numberer;
import javax.xml.transform.TransformerException;
public class MyInitializer implements Initializer {
public override void initialize(Configuration config) throws TransformerException {
config.setLocalizerFactory(new LocalizerFactory() {
public Numberer getNumberer(final String language, final String country) {
if (language.equals("de")) {
return Numberer_de.getInstance();
} else {
return null;
}
}
});
}
}
compile that, put it on the classpath and then run e.g. java -cp saxon-he-10.3.jar;com/example/MyInitializer;net/sf/saxon/option/local/Numberer_de net.sf.saxon.Transform -init:com.example.MyInitializer -s:source.xml -xsl:stylesheet.xslt -o:result.txt.
Or you can subclass net.sf.saxon.Transform.
On the other hand, if you don't want to run Saxon from the command line but from the JAXP API then I think one approach is to create the Configuration e.g.
Configuration config = new Configuration();
config.setLocalizerFactory(new LocalizerFactory() {
public Numberer getNumberer(final String language, final String country) {
if (language.equals("de")) {
return Numberer_de.getInstance();
} else {
return null;
}
}
});
TransformerFactory transformerFactory = new TransformerFactoryImpl(config);
Templates templates = transformerFactory.newTemplates(xsltSource);
What was missing, was how to inject the Config. This worked for me:
import net.sf.saxon.Configuration;
import net.sf.saxon.TransformerFactoryImpl;
import net.sf.saxon.lib.Feature;
final TransformerFactoryImpl factory = (TransformerFactoryImpl) TransformerFactory.newInstance();
factory.getProcessor().setConfigurationProperty(Feature.CONFIGURATION, config);
I try to get some data from a dbus service and work with it in Java.
I can get the information in cli with the following command:
dbus-send --print-reply --system --dest=com.victronenergy.solarcharger.ttyUSB0 /Dc/0/Voltage com.victronenergy.BusItem.GetValue
The result is:
method return time=1538903662.321580 sender=:1.14 -> destination=:1.806 serial=335692 reply_serial=2
variant double 13.43
What I tried to get this data in Java, is:
After hours of reading, I created an Interface.
package javadbus;
import java.util.Map;
import org.freedesktop.dbus.DBusInterface;
import org.freedesktop.dbus.DBusSignal;
import org.freedesktop.dbus.Variant;
import org.freedesktop.dbus.exceptions.DBusException;
public interface BusItem extends DBusInterface
{
public static class PropertiesChanged extends DBusSignal
{
public final Map<String,Variant> changes;
public PropertiesChanged(String path, Map<String,Variant> changes) throws DBusException
{
super(path, changes);
this.changes = changes;
}
}
public String GetDescription(String language, int length);
public Variant GetValue();
public String GetText();
public int SetValue(Variant value);
public Variant GetMin();
public Variant GetMax();
public int SetDefault();
public Variant GetDefault();
}
Here I call getConnection() and getRemoteObject() successfully.
package javadbus;
import org.freedesktop.dbus.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.Variant;
public class VictronEnergyDBusSolarCharger {
private String port;
private DBusConnection conn;
public VictronEnergyDBusSolarCharger(String port) {
this.port = port;
try {
this.conn = DBusConnection.getConnection(DBusConnection.SYSTEM);
} catch (DBusException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String getData(String item) {
BusItem bi;
String data = null;
Variant vData = null;
try {
bi = (BusItem)conn.getRemoteObject("com.victronenergy.solarcharger." + this.port, item, BusItem.class);
vData = bi.GetValue();
//data = bi.GetText();
} catch (DBusException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return data;
}
...
}
It was a big task to resolve all dependecies and get the code compiled. But finaly I did it. So, javac now runs without errors.
But if I try to call the Method GetValue(), I get the following Exception:
[Sender] INFO org.freedesktop.dbus.MessageWriter - <= MethodCall(0,1) { Path=>/org/freedesktop/DBus, Interface=>org.freedesktop.DBus, Member=>Hello, Destination=>org.freedesktop.DBus } { }
[Sender] INFO org.freedesktop.dbus.MessageWriter - <= MethodCall(0,3) { Path=>/Dc/0/Voltage, Interface=>javadbus.BusItem, Member=>GetValue, Destination=>com.victronenergy.solarcharger.ttyUSB0 } { }
Exception in thread "main" org.freedesktop.DBus$Error$UnknownMethod: Method "GetValue" with signature "" on interface "javadbus.BusItem" doesn't exist
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.freedesktop.dbus.Error.getException(Error.java:141)
at org.freedesktop.dbus.Error.throwException(Error.java:171)
at org.freedesktop.dbus.RemoteInvocationHandler.executeRemoteMethod(RemoteInvocationHandler.java:158)
at org.freedesktop.dbus.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:222)
at com.sun.proxy.$Proxy1.GetValue(Unknown Source)
at javadbus.VictronEnergyDBusSolarCharger.getData(VictronEnergyDBusSolarCharger.java:28)
at javadbus.VictronEnergyDBusSolarCharger.getDcV(VictronEnergyDBusSolarCharger.java:38)
at javadbus.MainClass.main(MainClass.java:7)
Is it necessary to make a implementation of this Method GetValue? But why e.g. how should I do this? I only want to get this Information and not provide it like a Server.
Why was it a big task to get all dependencies?
dbus-java library and dependencies are all available at maven central, so a proper maven project should just work out-of-the-box.
Back to topic:
You don't have to implement GetValue(), but you need a suitable java interface for BusItem.
As far as I can see in the documentation of victronenergy (https://www.victronenergy.com/live/open_source:ccgx:d-bus) , your interface is not correct.
You provide SetDefault()/GetDefault() methods, which are only available on com.victronenergy.settings Objects, but you want to retrieve a com.victronenergy.BusItem (no part of the com.victronenergy.settings package).
This is one error. The second error is: you use the wrong package name for your BusItem class.
In your case DBus will try to resolve an object with the path javadbus.BusItem which is not provided by the connected BusAddress com.victronenergy.solarcharger.ttyUSB0.
The BusItem class has to be in package com.victronenergy or you have to use the annotation #DBusInterfaceName("com.victronenergy.BusItem").
The annotation will tell the DBus library to ignore the java package/class name and use the one provided in the annotation.
The Inteface BusItem had been created by CreateInterface-Script from https://dbus.freedesktop.org/doc/dbus-java/dbus-java/dbus-javase10.html and the XML from Introspect()
But you solved my real problem. I used the annotation #DBusInterfaceName("com.victronenergy.BusItem") now. No Exception anymore an i get data from my solarcharger. Thank you so much!
How to make java logging at the same folder of the JAR?
Thank you!
import java.util.logging.*;
public class Worker {
private static final Logger logger = Logger.getLogger(Worker.class.getName());
public static void main(String[] args) {
logger.info("Logging begins...");
try {
throw new Exception("Simulating an exception");
} catch (Exception ex){
logger.log(Level.SEVERE, ex.getMessage(), ex);
}
logger.info("Done...");
}
}
Add a Handler for the Logger you're using. FileHandler is particularly good here:
Handler h = new FileHandler("my-log.log");
h.setFormatter(new SimpleFormatter()); //set format to what you normally see
logger.addHandler(h);
It will use the system property java.util.logging.SimpleFormatter.format for formatting the text, so you can either adjust the property to your use, or implement Formatter and set one yourself.
An example using the property would be:
"[%1$tH:%1$tM:%1$tS] %4$s: %5$s%n"
Which is a format I commonly see.
With Spring 4 and Hibernate 4, I was able to use Reflection to get the Hibernate Configuration object from the current environment, using this code:
#Autowired LocalContainerEntityManagerFactoryBean lcemfb;
EntityManagerFactoryImpl emf = (EntityManagerFactoryImpl) lcemfb.getNativeEntityManagerFactory();
SessionFactoryImpl sf = emf.getSessionFactory();
SessionFactoryServiceRegistryImpl serviceRegistry = (SessionFactoryServiceRegistryImpl) sf.getServiceRegistry();
Configuration cfg = null;
try {
Field field = SessionFactoryServiceRegistryImpl.class.getDeclaredField("configuration");
field.setAccessible(true);
cfg = (Configuration) field.get(serviceRegistry);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
SchemaUpdate update = new SchemaUpdate(serviceRegistry, cfg);
With Hibernate 5, I must use some MetadataImplementor, which doesn't seems to be available from any of those objects. I also tried to use MetadataSources with the serviceRegistry. But it did say that it's the wrong kind of ServiceRegistry.
Is there any other way to get this working?
Basic idea for this problem is:
implementation of org.hibernate.integrator.spi.Integrator which stores required data to some holder. Register implementation as a service and use it where you need.
Work example you can find here https://github.com/valery-barysok/spring4-hibernate5-stackoverflow-34612019
create org.hibernate.integrator.api.integrator.Integrator class
import hello.HibernateInfoHolder;
import org.hibernate.boot.Metadata;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
public class Integrator implements org.hibernate.integrator.spi.Integrator {
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
HibernateInfoHolder.setMetadata(metadata);
HibernateInfoHolder.setSessionFactory(sessionFactory);
HibernateInfoHolder.setServiceRegistry(serviceRegistry);
}
#Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
}
}
create META-INF/services/org.hibernate.integrator.spi.Integrator file
org.hibernate.integrator.api.integrator.Integrator
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... args) throws Exception {
new SchemaExport((MetadataImplementor) HibernateInfoHolder.getMetadata()).create(true, true);
new SchemaUpdate(HibernateInfoHolder.getServiceRegistry(), (MetadataImplementor) HibernateInfoHolder.getMetadata()).execute(true, true);
}
}
I would like to add up on Aviad's answer to make it complete as per OP's request.
The internals:
In order to get an instance of MetadataImplementor, the workaround is to register an instance of SessionFactoryBuilderFactory through Java's ServiceLoader facility. This registered service's getSessionFactoryBuilder method is then invoked by MetadataImplementor with an instance of itself, when hibernate is bootstrapped. The code references are below:
Service Loading
Invocation of getSessionFactoryBuilder
So, ultimately to get an instance of MetadataImplementor, you have to implement SessionFactoryBuilderFactory and register so ServiceLoader can recognize this service:
An implementation of SessionFactoryBuilderFactory:
public class MetadataProvider implements SessionFactoryBuilderFactory {
private static MetadataImplementor metadata;
#Override
public SessionFactoryBuilder getSessionFactoryBuilder(MetadataImplementor metadata, SessionFactoryBuilderImplementor defaultBuilder) {
this.metadata = metadata;
return defaultBuilder; //Just return the one provided in the argument itself. All we care about is the metadata :)
}
public static MetadataImplementor getMetadata() {
return metadata;
}
}
In order to register the above, create simple text file in the following path(assuming it's a maven project, ultimately we need the 'META-INF' folder to be available in the classpath):
src/main/resources/META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory
And the content of the text file should be a single line(can even be multiple lines if you need to register multiple instances) stating the fully qualified class path of your implementation of SessionFactoryBuilderFactory. For example, for the above class, if your package name is 'com.yourcompany.prj', the following should be the content of the file.
com.yourcompany.prj.MetadataProvider
And that's it, if you run your application, spring app or standalone hibernate, you will have an instance of MetadataImplementor available through a static method once hibernate is bootstraped.
Update 1:
There is no way it can be injected via Spring. I digged into Hibernate's source code and the metadata object is not stored anywhere in SessionFactory(which is what we get from Spring). So, it's not possible to inject it. But there are two options if you want it in Spring's way:
Extend existing classes and customize all the way from
LocalSessionFactoryBean -> MetadataSources -> MetadataBuilder
LocalSessionFactoryBean is what you configure in Spring and it has an object of MetadataSources. MetadataSources creates MetadataBuilder which in turn creates MetadataImplementor. All the above operations don't store anything, they just create object on the fly and return. If you want to have an instance of MetaData, you should extend and modify the above classes so that they store a local copy of respective objects before they return. That way you can have a reference to MetadataImplementor. But I wouldn't really recommend this unless it's really needed, because the APIs might change over time.
On the other hand, if you don't mind building a MetaDataImplemetor from SessionFactory, the following code will help you:
EntityManagerFactoryImpl emf=(EntityManagerFactoryImpl)lcemfb.getNativeEntityManagerFactory();
SessionFactoryImpl sf=emf.getSessionFactory();
StandardServiceRegistry serviceRegistry = sf.getSessionFactoryOptions().getServiceRegistry();
MetadataSources metadataSources = new MetadataSources(new BootstrapServiceRegistryBuilder().build());
Metadata metadata = metadataSources.buildMetadata(serviceRegistry);
SchemaUpdate update=new SchemaUpdate(serviceRegistry,metadata); //To create SchemaUpdate
// You can either create SchemaExport from the above details, or you can get the existing one as follows:
try {
Field field = SessionFactoryImpl.class.getDeclaredField("schemaExport");
field.setAccessible(true);
SchemaExport schemaExport = (SchemaExport) field.get(serviceRegistry);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
Take a look on this one:
public class EntityMetaData implements SessionFactoryBuilderFactory {
private static final ThreadLocal<MetadataImplementor> meta = new ThreadLocal<>();
#Override
public SessionFactoryBuilder getSessionFactoryBuilder(MetadataImplementor metadata, SessionFactoryBuilderImplementor defaultBuilder) {
meta.set(metadata);
return defaultBuilder;
}
public static MetadataImplementor getMeta() {
return meta.get();
}
}
Take a look on This Thread which seems to answer your needs
Well, my go to on this:
public class SchemaTranslator {
public static void main(String[] args) throws Exception {
new SchemaTranslator().run();
}
private void run() throws Exception {
String packageName[] = { "model"};
generate(packageName);
}
private List<Class<?>> getClasses(String packageName) throws Exception {
File directory = null;
try {
ClassLoader cld = getClassLoader();
URL resource = getResource(packageName, cld);
directory = new File(resource.getFile());
} catch (NullPointerException ex) {
throw new ClassNotFoundException(packageName + " (" + directory + ") does not appear to be a valid package");
}
return collectClasses(packageName, directory);
}
private ClassLoader getClassLoader() throws ClassNotFoundException {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
return cld;
}
private URL getResource(String packageName, ClassLoader cld) throws ClassNotFoundException {
String path = packageName.replace('.', '/');
URL resource = cld.getResource(path);
if (resource == null) {
throw new ClassNotFoundException("No resource for " + path);
}
return resource;
}
private List<Class<?>> collectClasses(String packageName, File directory) throws ClassNotFoundException {
List<Class<?>> classes = new ArrayList<>();
if (directory.exists()) {
String[] files = directory.list();
for (String file : files) {
if (file.endsWith(".class")) {
// removes the .class extension
classes.add(Class.forName(packageName + '.' + file.substring(0, file.length() - 6)));
}
}
} else {
throw new ClassNotFoundException(packageName + " is not a valid package");
}
return classes;
}
private void generate(String[] packagesName) throws Exception {
Map<String, String> settings = new HashMap<String, String>();
settings.put("hibernate.hbm2ddl.auto", "drop-create");
settings.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL94Dialect");
MetadataSources metadata = new MetadataSources(
new StandardServiceRegistryBuilder()
.applySettings(settings)
.build());
for (String packageName : packagesName) {
System.out.println("packageName: " + packageName);
for (Class<?> clazz : getClasses(packageName)) {
System.out.println("Class: " + clazz);
metadata.addAnnotatedClass(clazz);
}
}
SchemaExport export = new SchemaExport(
(MetadataImplementor) metadata.buildMetadata()
);
export.setDelimiter(";");
export.setOutputFile("db-schema.sql");
export.setFormat(true);
export.execute(true, false, false, false);
}
}
Before asking this question I tried to follow the following questions which are similar:
Injecting Properties using Spring & annotation #Value
How can I inject a property value into a Spring Bean which was configured using annotations?
Loading up properties file to a class in Spring
However, in my case I am not using any web applications or Tomcat; I'm just trying to load a cluster.properties file into a regular Java project via Spring so I can then ingest dummy data into Accumulo. Also, I'm trying to load properties from a cluster.properties file, not from key value pairs defined in an xml file.
Using what I learned from the links above and lots of reading on Spring, here's what I have:
I created the following context.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<!-- Define the Spring Bean to load our cluster properties -->
<bean id="props" class="accumuloIngest.LoadProperties"></bean>
</beans>
And here is a small snippet of what my cluster.properties file looks like:
cluster.instance=instance
cluster.username=user
etc...
Next, I created the following Spring main method under the class MainApp.java:
package accumuloIngest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
// Spring main method used to load a cluster.properties file with the Spring framework
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
LoadProperties myObj = LoadProperties.class.cast(ctx.getBean("props"));
// Now print out the cluster.properties loaded by Spring to verify they aren't null
StringBuffer springPropsBuffer = new StringBuffer();
springPropsBuffer.append("Printing out cluster.properties read via Spring...");
springPropsBuffer.append("\n\n");
springPropsBuffer.append("instanceName= ");
springPropsBuffer.append(myObj.getInstanceName());
springPropsBuffer.append("\n");
springPropsBuffer.append("userName= ");
springPropsBuffer.append(myObj.getUserName());
springPropsBuffer.append("\n");
springPropsBuffer.append("password= ");
springPropsBuffer.append(myObj.getPassword());
springPropsBuffer.append("\n");
springPropsBuffer.append("zooServers= ");
springPropsBuffer.append(myObj.getZooServers());
springPropsBuffer.append("\n");
springPropsBuffer.append("tableName= ");
springPropsBuffer.append(myObj.getTableName());
springPropsBuffer.append("\n");
springPropsBuffer.append("dataFile= ");
springPropsBuffer.append(myObj.getDataFile());
springPropsBuffer.append("\n");
springPropsBuffer.append("dataDelim= ");
springPropsBuffer.append(myObj.getDataDelim());
springPropsBuffer.append("\n");
springPropsBuffer.append("rowCount= ");
springPropsBuffer.append(myObj.getRowCount());
springPropsBuffer.append("\n");
System.out.println(springPropsBuffer.toString());
// now start data ingest
myObj.startIngest(); // method that calls Ingester class to start data ingest
} // end of main method
} // end of MainApp class
Spring loads my context.xml file and loads the Bean I called "props", but the values are still null. It seems that my #Value annotations aren't working in my LoadProperties class:
package accumuloIngest;
import java.io.IOException;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.TableExistsException;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class LoadProperties {
// this class defines the Spring Bean and loads the cluster properties
// using the SpringFramework
#Bean
public static PropertyPlaceholderConfigurer props(){
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Resource[] resource = new ClassPathResource[ ]
{ new ClassPathResource("/EclipseProjectName/src/cluster.properties") };
ppc.setLocations(resource);
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
// Now load the properties from cluster.properties using the Spring Framework
private #Value("${cluster.instance}") String instanceName;
private #Value("${cluster.username}") String userName;
private #Value("${cluster.password}") String password;
private #Value("${cluster.zooServers}") String zooServers;
private #Value("${cluster.TableName}") String tableName;
private #Value("${cluster.DataFile}") String dataFile;
private #Value("${cluster.DataDelimiter}") String dataDelim;
private #Value("${cluster.rowCount}") int rowCount;
// Getters for the other Java classes to access properties loaded by Spring
public String getInstanceName() {
return instanceName;
}
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
public String getZooServers() {
return zooServers;
}
public String getTableName() {
return tableName;
}
public String getDataFile() {
return dataFile;
}
public String getDataDelim() {
return dataDelim;
}
public int getRowCount() {
return rowCount;
}
// method to kick off the ingest of dummy data
void startIngest() {
Ingester ingestObject = new Ingester();
try {
ingestObject.ingestData();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TableNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TableExistsException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AccumuloException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AccumuloSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // end of try-catch block
} // end of startIngest method
} // end of LoadProperties class
Yet when I run MainApp.java in Eclipse the values are null when my Ingester.java class calls the getters.
Here's the console output when I run MainApp.java in Eclipse:
13/09/24 14:08:24 INFO support.ClassPathXmlApplicationContext: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext#191f667c: startup date [Tue Sep 24 14:08:24 EDT 2013]; root of context hierarchy
13/09/24 14:08:24 INFO xml.XmlBeanDefinitionReader: Loading XML bean definitions from class path resource [context.xml]
13/09/24 14:08:24 INFO support.DefaultListableBeanFactory: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#3cdd17f5: defining beans [props]; root of factory hierarchy
Printing out cluster.properties read via Spring...
instanceName= null
userName= null
password= null
zooServers= null
tableName= null
dataFile= null
dataDelim= null
rowCount= 0
Exception in thread "main" java.lang.IllegalArgumentException: argument was null:Is null- arg1? true arg2? true
at org.apache.accumulo.core.util.ArgumentChecker.notNull(ArgumentChecker.java:36)
at org.apache.accumulo.core.client.ZooKeeperInstance.<init>(ZooKeeperInstance.java:99)
at org.apache.accumulo.core.client.ZooKeeperInstance.<init>(ZooKeeperInstance.java:85)
at accumuloIngest.Ingester.ingestData(Ingester.java:65)
at accumuloIngest.LoadProperties.startIngest(LoadProperties.java:69)
at accumuloIngest.MainApp.main(MainApp.java:44)
Am I missing a piece of the Spring framework that loads the properties in my cluster.properties file? I had tried adding #AutoWired to both my MainApp and LoadProperties java classes but that didn't seem to help.
If you're going to use #Bean, you'll need #Configuration. You shouldn't declare a xml context to include an annotation context. You also shouldn't use your #Configuration class instance as a bean. ClassPathXmlApplicationContext is no good for processing annotation based configurations.
Use something like the following
#Configuration
#ComponentScan(basePackageClasses = LoadProperties.class)
public static class Config {
#Bean
public static PropertyPlaceholderConfigurer props() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Resource[] resource = new ClassPathResource[] { new ClassPathResource(
"/EclipseProjectName/src/cluster.properties") };
ppc.setLocations(resource);
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
#Bean
public LoadProperties loadProperties() {
return new LoadProperties();
}
}
public static class LoadProperties {
private #Value("${cluster.zooServers}") String zooServers;
... // getters and setters
}
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
LoadProperties load = (LoadProperties) context.getBean(LoadProperties.class);
System.out.println(load.getZooServers());
}
A few things to note:
In your ClassPathResource you need to specify a classpath resource. Do you really have a resource /EclipseProjectName/src/cluster.properties at the root of your classpath? I very much doubt it.
In this case you won't need a #ComponentScan, but familiarize yourself with it.
A PropertyPlaceholderConfigurer needs to be declared static so it can be initialized before the other #Bean declarations. You should use PropertySourcesPlaceholderConfigurer as explained in the javadoc.