I am trying to put together a basic SPI-based registry of Handlers, which I lookup from a HandlerRegistry. When I use the ServiceLoader.load(Handler.class) to initialize the providers, and then iterate the list to lazily load them, I am not seeing any instances of the class. Keeping this as simple as possible, my HandlerRegistry class is:
public class HandlerRegistry
{
private static HandlerRegistry registry;
private ServiceLoader<Handler> handlerLoader;
private HandlerRegistry()
{
handlerLoader = ServiceLoader.load(Handler.class);
}
public static synchronized HandlerRegistry getRegistry()
{
if (registry == null) {
registry = new HandlerRegistry();
registry.init();
}
return registry;
}
private void init()
{
System.out.println("HandlerRegistry.init()");
}
public Handler lookup(String item)
{
System.out.println("lookup("+item+")");
try {
Iterator<Handler> it = handlerLoader.iterator();
while (it.hasNext()) {
Handler handler = it.next();
System.out.println("found handler "+handler);
}
}
catch (ServiceConfigurationError err) {
err.printStackTrace();
}
return null;
}
}
I have a com.example.handler.Handler interface (empty for now for simplicity), and a com.example.handler.handlers.DummyHandler class which implements that interface. I have created a file in my jar called META-INF/services/com.example.handler.Handler, which contains the single line
com.example.handler.handlers.DummyHandler
according to the javadoc. My unit test simply calls the lookup() method to verify looking up the handler for an item. Of course there will eventulaly need to be a check of some kind to see if this is the right handler for this item, but at this point I am not even seeing my DummyHandler class get loaded by the registry. Am I doing something wrong here?
Thanks!
The answer appears to be in the sensitivity to exactly how this is configured. I had been placing my provider name resource file (the one named com.example.handler.Handler) directly in the top level project directory, i.e., /resources/META-INF/services/com.example.handler.Handler. I had configured my build.gradle to pull the file out and put it into the jar:
jar { from('resources') { include 'META-INF/services/*.*' } }
When I inspected the jar file, the file was there, right where I expected it to be, so I thought all was well. On a kick, I happened to move the resources folder from down under src/main, and presto! it works. I inspected the jar file and it appears identical to one built the previous way, but for some reason this one works. I will update further if I can determine a difference, but at least my test case works now.
Related
I would like to create a proper properties management strategy in a java webapp that relays on google guice as a DI framework.
I would like to have a mechanism answering the following 3 requirements:
I would like to be able to inject properties using guice (#Named)
I would like to be able to access properties in a static way
The mechanism should support prioritization of properties, meaning that a property can be wrapped in the deployed war with a certain value but it can also be redundant in the target system level or local file system (of the target machine I deploy on), in such a case the value in the war will be overridden by the value that exists in the target machine.
I believe this is a standard requirement. Now, using guice standard binder I can easily get the first requirement but not the other two. To get the other two I created my own class that does the following:
Wraps and exposes the binding methods of guice (those that binds properties) For example:
public static void bindString(AnnotatedBindingBuilder<String> binder, String property, String defaultValue) {
binder.annotatedWith(Names.named(property)).toInstance(getProperty(property, defaultValue));
}
Where the getProperty method knows how to handle my properties (get the value from the war or system level) and exposes the properties statically as well.
So basically as long as I'm using this utility that I created for properties bindings I'm good, it covers all my requirements but once I use the standard guice bindings I'm losing the second and third requirement.
Is there a way to override guice bindings and get all those 3 requirements?
Once I had the same challange in a spring based app and was pretty easy. I implemented ApplicationContextInitializer with the following method:
#Override
public void initialize(ConfigurableWebApplicationContext ctx) {
PropertySource<Map<String, Object>> localProps = null;
try {
localProps = new ResourcePropertySource(new ClassPathResource(LOCAL_PROPERTIES_FILE_NAME));
} catch (IOException e) {
LOG.fatal("Could not load local properties from classpath " + LOCAL_PROPERTIES_FILE_NAME);
return;
}
LOG.info("Loaded configuration from classpath local file " + LOCAL_PROPERTIES_FILE_NAME);
ctx.getEnvironment().getPropertySources().addFirst(localProps);
}
so this gave me a way to add local properties with highest priority to my Environment. In case of overlap with war properties the local ones had higher priority. In addition I exposed my Environment statically so I has static access to my properties (for services that are not managed by the container, legacy mostly).
How can I achieve this with guice?
Unfortunately, I don't think that you are going to find anything that gives you a truly clean and satisfying implementation. Especially, I don't think that you will find anything that gives you exactly what you want without implementing at least portions of it yourself.
If I had those needs, I would make sure that my injector is created in a central InjectorFactory. If you require a large number of parameters from outside to create your injector, I would simply create it once at the very beginning of my application and then cache the injector into a static final field. This would make it available to a static method. I would bind my "fall-back" property loading to an explicit provider. That way, instead of using the standard Names.bindProperties(...) method, I would bind it directly to a Provider. This provider then implements the logic that is necessary to perform the fallback or to merge multiple property files. Having the injector cached to a static field means that I can call a static method to access properties from a global-context outside of my injected classes.
Using your own provider seems initially unpleasant, but can provide some additional benefits. For starters, you can implement your fallback strategy exactly how you want. Additionally, you can add additional behaviors such as auto-reloading your property files, etc (not shown in my code sample).
public class InjectorFactory {
private static Injector injector = null;
public static synchronized Injector getOrCreateInjector() {
if(injector == null) {
injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
Properties properties1 = createProperties("file1.properties");
Properties properties2 = createProperties("file2.properties");
Set<Object> propertyNames = new HashSet<Object>();
propertyNames.addAll(properties1.keySet());
propertyNames.addAll(properties2.keySet());
for (Object object : propertyNames) {
String propertyName = (String) object;
bind(String.class).annotatedWith(Names.named(propertyName)).toProvider(new StringProvider(properties1, properties2, propertyName));
}
}
private Properties createProperties(String propertyFileName) {
try {
InputStream stream = InjectorFactory.class.getResourceAsStream(propertyFileName);
try {
Properties properties = new Properties();
properties.load(stream);
return properties;
} finally {
stream.close();
}
} catch (IOException exception) {
throw new RuntimeException("Could not load properties file");
}
}
});
}
return injector;
}
public static String getProperty(String propertyName) {
return getOrCreateInjector().getInstance(Key.get(String.class, Names.named(propertyName)));
}
}
Given the above code and file1.properties:
property1=Property1Value
property2=Property2Value
And file.properties:
property2=IncorrectProperty2Value
property3=Property3Value
with the provider
public class StringProvider implements Provider<String> {
private Properties properties1;
private Properties properties2;
private String propertyName;
public StringProvider(Properties properties1, Properties properties2,
String propertyName) {
this.properties1 = properties1;
this.properties2 = properties2;
this.propertyName = propertyName;
}
public String get() {
if(properties1.containsKey(propertyName)) {
return properties1.getProperty(propertyName);
}
return properties2.getProperty(propertyName);
}
}
The following usage:
public class InjectorFactoryTest {
public static void main(String ... parameters) {
System.out.println(InjectorFactory.getProperty("property1"));
System.out.println(InjectorFactory.getProperty("property2"));
System.out.println(InjectorFactory.getProperty("property3"));
}
}
Outputs:
Property1Value
Property2Value
Property3Value
i am going to continue to express my seemingly endless missunderstanding in EJBs:
I am using JBoss 8 (Wildfly 8.1) as my Applicationserver.
I am currently building a workaround for the Problem, that Inpustreams cannot be passed to remote EJB via RMI (which makes absolute sense once i thought about it).
I i must not replace the existing Resources Interface (writing an Inpustream to a File) I build following 3 Projects as a solution:
Interfaces (library included in both EARs):
com.package.ejb
public interface StorageAdapter extends Serializable{
String store(Inpustream is); (//returns an id
Inputstream load(String id);
}
public interface StorageAdapterProvider{
StorageAdapter provide();
}
Persistence-EAR
com.package.impl
public class FileSystemStorageAdapter implements com.package.ejb.Storageadapter {
//implementation, writing to locally mounted path in filesystem...
}
com.package.impl
#Singleton
#Remote(com.package.ejb.StorageAdapterProvider.class)
public class StorageAdapterProviderBean implements com.package.ejb.StorageadapterProvider {
public StorageAdapter provide() {
return new FileSystemStorageAdapter();
}
}
Business-EAR
com.package.business
public class StorageProvider {
#EJB(looklup = "java:global/Persistence-EAR/StorageAdapterProviderBean!com.package.ejb.StorageAdapterProvider"
private StorageAdapterProvider provider;
#Produces
public StorageAdapter getStorageAdapter() {
return provider.provide();
}
}
I then use #Inject StorageAdapter storageAdapter; to get an instance of the Storageadapter-implementation.
Business-EAR/Storageprovider then throws a ClassCastException, telling me that com.package.impl.FileSystemStorageAdapter cannot be cast to com.package.ejb.StorageAdapter
i added some logging to the EJB
com.package.impl
#Singleton
#Remote(com.package.ejb.StorageAdapterProvider.class)
public class StorageAdapterProviderBean implements com.package.ejb.StorageadapterProvider {
public StorageAdapter provide() {
StroageAdapter ret = new FileSystemStorageAdapter();
logger.info("EJB: RETURNING Stortage adapter");
logger.info(" is of type:"+ret.getClass().getName());
logger.info(" is Storageadapter: "+ (ret instanceof StorageAdapter));
logger.info(" is FileSystemStorageAdapter: "+ (ret instanceof FileSystemStorageAdapter));
return ret;
}
}
and it correctly prints:
EJB: RETURNING Stortage adapter
is of type:com.package.impl.FileSystemStorageAdapter
is Storageadapter: true
is FileSystemStorageAdapter: true
Further investigation shows, that the Exception is thrown while the container is Wrapping the "ret" Object, as i also get the Exeption if i chnage the #Provides implementation to:
Object o = provider.provide();
Is it impossible to return an Interface type from an EJB?
What am i missing?
Does Business-EJB need to know the implementing Classes? - Wouldn't a ClassNotFound Exception make more sense in this case?
Thanks in advance!
EDIT:
My Packaging looks as follows:
Persistence.ear
-lib/Interfaces.jar
-StorageAdapter
-Storageprovider
-persistence_ejb.jar
-FileSystemStorageAdapter
-StorageAdapterProviderBean
Business.ear
-lib/Interfaces.jar
-StorageAdapter
-Storageprovider
-business_ejb.jar
-StorageProvider
-web.war
-Jaxrsres
The Jaxrsres is the one having a Storageadapter Injected via #Inject
I am deploying these 2 wars to a JBoss Wildlfly 8.1 Server.
I did not make any relevant changes to the configuration - so all the other Point you requested are defaults as far as I understand.
EDIT2:
It somewhat defeats the purpose, but it works if i add the Persistence.ear/persistence_ejb.jar to the libraries of Business.ear.
Business.ear
-lib
-Interfaces.jar
-StorageAdapter
-Storageprovider
-persistence_ejb.jar
-FileSystemStorageAdapter
-StorageAdapterProviderBean
-business_ejb.jar
-StorageProvider
-web.war
-Jaxrsres
This is obviously not what I want though :/
The main Point is, i want to be able to redeploy Persistence.ear with, say, "DatabaseStorageAdapter", without touching business.ear.
The "Dream" is to Provide the Storageadapter as an EJB Directly - but as the Interface is fixed and needs an InputStream, this cannot be done.
I'm new to Java and I'm jumping right in the deep end and using Guice in a project but I can't work out how to bind different values to a constructor based on what parent object the current object will be injected into.
In my code I want to be able to pass in the path to some XSLT files in the constructor of the MessageTransformer but these should be different depending on what it's being injected into.
public class MessageTransformer {
public MessageTransformer(String XsltRequestPath, String XsltReplyPath {
....
}
}
public class SomeClassThatTransformsMessages {
public SomeClassThatTransformsMessages(Transformer transformer) {
....
}
public class SomeOtherClassThatTransformsMessages {
public SomeOtherClassThatTransformsMessages(Transforms transformer) {
....
}
}
So if I was doing it manually I'm trying to recreate the following:
variable1 = new SomeClassThatTransformsMessages (new Transformer("fileA", "fileB"));
variable2 = new SomeOtherClassThatTransformsMessages (new Transformer("fileC", "fileD"));
I don't think I can use the #Named attribute on the transformer but i can possibly use that on the parent classes but not sure how to wire up the files.
I've looked at all the bindings help but none appear to match.
Thanks
I chose to take properties file for customization of some settings.
I use the following code to make a Properties Object available in a class
Properties defaultProps = new Properties();
try {
FileInputStream in = new FileInputStream("custom.properties");
defaultProps.load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
Do I have to add this to every class? Probably not because then every class would open a stream to this file.
But I'm not sure how to handle this properly.
Should I make a class MyProperties and instantiate it in whatever class needs properties?
Thanks in advance!
Once you initialized defaultProps, you can make its contents available to other objects in your app e.g. via a public static accessor method, e.g.:
public class Config {
private static Properties defaultProps = new Properties();
static {
try {
FileInputStream in = new FileInputStream("custom.properties");
defaultProps.load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getProperty(String key) {
return defaultProps.getProperty(key);
}
}
This is the simplest approach, however it creates an extra dependency which makes unit testing harder (unless you provide a method in Config to set a mock property object for unit testing).
An alternative is to inject defaultProps (or individual configuration values from it) into each object which needs it. However, this may mean you need to add extra parameter(s) to lots of methods if your call hierarchies are deep.
If you only need one instance of your properties class you can use the singleton (anti?)-pattern.
It would look like a class like this:
public class MyProperties extends Properties {
private static MyProperties instance = null;
private MyProperties() {
}
public static MyProperties getInstance() {
if (instance == null) {
try {
instance = new MyProperties();
FileInputStream in = new FileInputStream("custom.properties");
instance.load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return instance;
}
}
Why not use a static ResourceBundle ?
static final ResourceBundle myResources =
ResourceBundle.getBundle("MyResources", currentLocale);
There's too little information to determine what the best way to handle this would be. You may want to expose it using an accessor, or pass it into each class that requires it. Alternatively, you may pull out the properties that each class needs and pass their values into the class's constructor.
Load the properties once using and store the Properties somewheres that others classes can pull from. If that is a MyProperties class that references a static variable somewhere that is fine.
This is a special case of making anything available globally. Using static methods is quite bad. A better but bad solution is using the sigleton pattern. Testing is the greatest problem here. IMHO, the best way is using Dependency injection, although it may be an overkill for small applications.
Since this information is static across all instances, I recommend implementing the Properties class as a singleton. By using the static initialization block method, you can have it load the file automatically when the program starts up.
public class Properties {
static {
try {
FileInputStream in = new FileInputStream("custom.properties");
load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
protected static void load(FileInputStream in) {
// existing load functionality here
}
}
You are still going to need an internal storage mechanism and accessor mechanism. These should also be marked static.
Rather than loading properties in every class. Load it somewhere around main() and pass it to other classes via their constructors.
Don't share them globally.
- Difficult to test
- Against the abstraction (Global access, DAO can access user settings. it should be prevented by passing only what it needs.. not everything)
- Classes lie what they need
I've been building a GWT 1.7 + GAE application using the eclipse plugin.
The system constants are loaded into a MyConstants.properties file that is loaded by the singleton MyConstants class extending the ...gwt.i18n.client.Constants class.
I would like for MyConstants to load one of several files containing settings like
MyConstants-local.properties
MyConstants-alpha.properties
MyConstants-beta.properties
MyConstants-prod.properties
I've found several references to Guice's Enum Stage but this does not seem to be supported by gin. Besides, it only handles dev/prod and I definitely need a local/beta/prod solution.
Is there a way to do this using a command line arg or some other instance defined runtime parameter that will work when loaded onto GAE?
On the GAE server side, I was able to distinguish my dev environment from any deployed production environment with these bits of code.
Create one interface and two class files.
public interface MyConstants {
public String myConstant();
}
public class MyConstantsDev implements MyConstants {
public String myConstant() { return "xyzzy-dev"; };
}
public class MyConstantsProd implements MyConstants {
public String myConstant() { return "xyzzy-prod"; };
}
Define a bit of Guice magic binding using the "user.dir" env var. The last directory within the user.dir path is either the unique Google App Engine Application Identifier or your root project development directory. Once you know this, you can determine which set of constants to use.
public class MyServerModule extends com.google.inject.AbstractModule {
String appIdentifier = new File( System.getProperty("user.dir") ).getName();
if ( appIdentifier.equals("trunk") ) {
// Load MyConstants-dev.properties
bind( MyConstants.class ).to( MyConstantsDev.class ).in(Singleton.class);
} else {
// Load MyConstants-prod.properties
bind( MyConstants.class ).to( MyConstantsProd.class ).in(Singleton.class);
}
}
This allows me to inject dev/prod constants into classes like this:
public class MyDomainClass {
#Inject
public MyDomainClass( Logger logger, MyConstants const ) {
logger.debug( const.myConstant() );
};
}
You should use the especially made type-safe interface to work this out...
if (SystemProperty.environment.value() == SystemProperty.Environment.Value.Production)
{
//It's production.
}
else
{
//It's Dev - and (SystemProperty.environment.value() == SystemProperty.Environment.Value.Development)
}
One thing that's different between the development and deployed environment is the SERVER_SOFTWARE environment variable:
if (System.getenv("SERVER_SOFTWARE").startsWith("Dev")) {
// Load MyConstants-dev.properties
} else {
// Load MyConstants-prod.properties
}
Maybe you can pick which Guice module to load based off of that.
On the above answer for getting the GAE SERVER_SOFTWARE variable; The SERVER_SOFTWARE environment Variable is a CGI default therefore its a web server setting and isn't accessible through the System environment vars but is available from the ServletContext.
Here's the code that should get you the value your needing (No idea why its ServerInfo and not ServerSoftware...):
if (getServletContext().getServerInfo().startsWith("Dev")) {
// Load MyConstants-dev.properties
} else {
// Load MyConstants-prod.properties
}