I'm trying to read deployment specific information from a properties file in my wildfly configuration folder. I tried this:
#Singleton
#Startup
public class DeploymentConfiguration {
protected Properties props;
#PostConstruct
public void readConfig() {
props = new Properties();
try {
props.load(getClass().getClassLoader().getResourceAsStream("my.properties"));
} catch (IOException e) {
// ... whatever
}
}
But apparently this is not working since the configuration folder is not in the classpath anymore. Now I can't find an easy way to do it. My favorite would be something like this:
#InjectProperties("my.properties")
protected Properties props;
The only solution I found on the web so far involves making my own OSGi module, but I believe there must be an easier way to do it (one without OSGi!). Can anyone show me how?
If you want to explicitly read a file from the configuration directory (e.g. $WILDFLY_HOME/standalone/configuration or domain/configuration) there's a system property with the path in it. Simply do System.getProperty("jboss.server.config.dir"); and append your file name to that to get the file.
You wouldn't read it as a resource though, so...
String fileName = System.getProperty("jboss.server.config.dir") + "/my.properties";
try(FileInputStream fis = new FileInputStream(fileName)) {
properties.load(fis);
}
Then the file would be loaded for you.
Also, since WildFly doesn't ship with OSGi support anymore, I don't know how creating an OSGi module would help you here.
Here is a full example using just CDI, taken from this site.
Create and populate a properties file inside the WildFly configuration folder
$ echo 'docs.dir=/var/documents' >> .standalone/configuration/application.properties
Add a system property to the WildFly configuration file.
$ ./bin/jboss-cli.sh --connect
[standalone#localhost:9990 /] /system-property=application.properties:add(value=${jboss.server.config.dir}/application.properties)
This will add the following to your server configuration file (standalone.xml or domain.xml):
<system-properties>
<property name="application.properties" value="${jboss.server.config.dir}/application.properties"/>
</system-properties>
Create the singleton session bean that loads and stores the application wide properties
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
#Singleton
public class PropertyFileResolver {
private Logger logger = Logger.getLogger(PropertyFileResolver.class);
private String properties = new HashMap<>();
#PostConstruct
private void init() throws IOException {
//matches the property name as defined in the system-properties element in WildFly
String propertyFile = System.getProperty("application.properties");
File file = new File(propertyFile);
Properties properties = new Properties();
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
logger.error("Unable to load properties file", e);
}
HashMap hashMap = new HashMap<>(properties);
this.properties.putAll(hashMap);
}
public String getProperty(String key) {
return properties.get(key);
}
}
Create the CDI Qualifier. We will use this annotation on the Java variables we wish to inject into.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR })
public #interface ApplicationProperty {
// no default meaning a value is mandatory
#Nonbinding
String name();
}
Create the producer method; this generates the object to be injected
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;
public class ApplicaitonPropertyProducer {
#Inject
private PropertyFileResolver fileResolver;
#Produces
#ApplicationProperty(name = "")
public String getPropertyAsString(InjectionPoint injectionPoint) {
String propertyName = injectionPoint.getAnnotated().getAnnotation(ApplicationProperty.class).name();
String value = fileResolver.getProperty(propertyName);
if (value == null || propertyName.trim().length() == 0) {
throw new IllegalArgumentException("No property found with name " + value);
}
return value;
}
#Produces
#ApplicationProperty(name="")
public Integer getPropertyAsInteger(InjectionPoint injectionPoint) {
String value = getPropertyAsString(injectionPoint);
return value == null ? null : Integer.valueOf(value);
}
}
Lastly inject the property into one of your CDI beans
import javax.ejb.Stateless;
import javax.inject.Inject;
#Stateless
public class MySimpleEJB {
#Inject
#ApplicationProperty(name = "docs.dir")
private String myProperty;
public String getProperty() {
return myProperty;
}
}
The simplest thing you can do is to run standalone.sh with a -P option referencing your properties file (you need a URL file:/path/to/my.properties, or put the file in $WILDFLY_HOME/bin).
Then all properties from the file will be loaded as system properties.
For injecting configuration properties into your application classes, have a look at DeltaSpike Configuration, which supports different property sources like system properties, environment variables, JNDI entries and hides the specific source from your application.
Alternatively, to avoid setting system properties (which will be global in the sense of being visible to all applications deployed to your WildFly instance), you can also define a custom property source for DeltaSpike reading a properties file from any given location, and these properties will be local to your application.
It sounds like the problem you are trying to solve is managing different (but probably similar) configuration files for running your application in different environments (ie, Production, QA, or even different customers). If that is the case, take a look at Jfig http://jfig.sourceforge.net/ . It would obviate the need for storing property files outside your classpath (but you still could).
What is needed is a hierarchical approach to configuration files. The ninety percent of configuration values that do not change can be maintained in a base file. The other ten percent (or less) may be maintained in their own distinct configuration file. At run time, the files are layered on top of each other to provide a flexible, manageable configuration. For example, in a development environment myhost.config.xml combines with dev.config.xml and base.config.xml to form my unique configuration.
Each configuration file may then be maintained in version control as they have unique names. Only the base files need to be modified when base values change, and it is easy to see the difference between versions. Another major benefit is that changes to the base configuration file will be exhaustively tested before deployment.
InputStream in = null;
File confDir = new File(System.getProperty("jboss.server.config.dir"));
File fileProp = new File(confDir, "my.properties");
try{
//teste fileProp.exists etc.
in = new FileInputStream(fileProp);
Properties properties = new Properties();
properties.load(in);
//You should throws or handle FileNotFoundException and IOException
}finally{
try{
in.close();
}catch(Exception ignored){
}
}
To avoid this kind of problem the issue is to set the jboss.server.config.dir in VM arguments like that :
-Djboss.server.config.dir="[jboss_repository]/server/[default-all-standard-standalone]/conf" –server
If you have in standalone.xml property:
<property name="my.properties" value="propertyValue"/>
you can wasily read it with:
static final String MY_PROPERTY = System.getProperty("my.properties");
Or if you specify context param in web.xml like:
<context-param>
<param-name>MyProperty</param-name>
<param-value>MyPropertyValue</param-value>
</context-param>
You can read it in Java bean:
String myProperty= getServletContext().getInitParameter("MyProperty");
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 have application properties file which I am dynamically updating using maven build step.
mvn clean -Dusername=user1 -Durl=xxxx -Dpassword=xxxx -DskipTests
install
jdbc.url=${url}
jdbc.username=${username}
jdbc.password=${password}
I am reading these properties in the configuration class
#Configuration
#ImportResource("classpath:/spring-beans.xml")
#PropertySource("classpath:/application.properties")
public class ApplicationConfiguration {
#Value("${jdbc.url}")
private String url;
#Value("${jdbc.username}")
private String username;
#Value("${jdbc.password}")
private String password;
#Bean(name = "c3p0DataSource")
public ComboPooledDataSource dataSource() throws PropertyVetoException,
IOException {
logger.info("Creating Datasource for {}",System.getenv("SPRING_DATASOURCE_URL"));
// logger.info("Creating Datasource for username {}",
prop.getProperty("username"));
logger.info("Creating Datasource for {}", System.getenv("username"));
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
logger.info("User Name :" + username);//returning $username instead of user1
logger.info("password :" + password);
System.out.println("User name : " + username);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource; } }
I am not getting updated values instead I am getting $username, $password as values, can anyone help me what i am missing here ?
my modified properties file look like below
jdbc.url=xxxx
jdbc.username=user1
jdbc.password=xxxx
you should run
mvn clean -Djdbc.username=user1 -Djdbc.url=xxxx -Djdbc.password=xxxx -DskipTests install
Rather than using Spring's application property , I will suggest you to use another property file, store it on file system and use org.apache.commons.configuration.PropertiesConfiguration class to load values from this file.
org.apache.commons.configuration.PropertiesConfiguration has capability to reload property file on change.
https://commons.apache.org/proper/commons-configuration/userguide/howto_properties.html
If you are using maven add below dependency.
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
When you say 'dynamically updated' it seems to me you just mean updated at build time and not at runtime. If so then you need to use the maven resources plugin, define the maven variables and use a different syntax in the properties file. This is covered in the properties and configuration section of the spring boot documentation
I have tried initialising data manually, which works. You can also give it a try.
You can try out the code below:
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Configuration;
#Configuration
#PropertySource("classpath:application.properties")
public class ApplicationConfiguration {
private Properties properties = new Properties();
public static String driverClass;
public static String dataSourceUrl;
public static String dataSourceUser;
public static String dataSourcePassword;
public ApplicationConfiguration() throws IOException {
properties.load(new InputStreamReader(ApplicationConfiguration.class.getResourceAsStream("/application.properties")));
driverClass = properties.getProperty("spring.datasource.driver-class-name");
dataSourceUrl = properties.getProperty("spring.datasource.url");
dataSourceUser = properties.getProperty("spring.datasource.username");
dataSourcePassword = properties.getProperty("spring.datasource.password");
}
// Other Code Details
}
Now I can easily use it like: ApplicationConfiguration.driverClass or ApplicationConfiguration.dataSourceUser.
Few other resources are also used by me from application.properties which I am not initialising manually and also not required while building jar. So only I am using #PropertySource("classpath:application.properties") to use other resources without initialising manually.
Try it once, It may help you :)
I'd like to accomplish this: Environment Specific application.properties file in Spring Boot application
in a Spring non-Boot application. Any idea on how to do that? Now I am setting environment variables to tell the application which properties to use, would prefer to do it the "boot" way.
Help would be appreciated.
In order to represent the several environments use profiles. If you want to know more browse this site. and I think this is exactly what you are looking for.
Update 1:
Considering you have a fixed suffix of your property files and you have a set of property files for different environment, for example,
development-it_wroks.properties,
test-it_wroks.properties etc. etc.
etc.it_wroks
is the suffix
Determine the active enviourment from active_env.properties
profiles.active: development
#profiles.active: test
#profiles.active: stage
#profiles.active: production
Write a custom Property resolver
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
public class MyPropertyUtil {
public static String getValuesFromPerpertyFile(String filename,String key){
String value = null;
Configuration config = getConfiguration(filename);
value = config.getString(key);
return value;
}
public static Configuration getConfiguration(String file){
Configuration config = null;
try{
Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration>
builder =new FileBasedConfigurationBuilder
<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.properties().setFileName(file));
config = builder.getConfiguration();
}catch(Exception ex){
ex.printStackTrace();
}finally{
}
return config;
}
}
Now your calling class
import org.apache.log4j.Logger;
public class MyCallingClass {
final static Logger logger = Logger.getLogger(this.getClass());
//Determine the active enviourment,You may determine this from os environment variable if you want
String activeEnvironment = MyPropertyUtil.
getValuesFromPerpertyFile("resource/active_env.properties"
,"profiles.active");
//Set the property file
String myEnvSpecificValue = MyPropertyUtil.
getValuesFromPerpertyFile("resource/"+activeEnvironment+"it_wroks.properties",
"my.property.string");
//Do what you want to
logger.info(myEnvSpecificValue);
}
You can add application-environment.properties as per environment. Spring boot should automatically detect the corresponding properties file based on active environment.
I would really like to use YAML config for Spring Boot, as I find it quite readable and useful to have a single file showing what properties are active in my different profiles. Unfortunately, I'm finding that setting properties in application.yml can be rather fragile.
Things like using a tab instead of spaces will cause properties to not exist (without warnings as far as I can see), and all too often I find that my active profiles are not being set, due to some unknown issue with my YAML.
So I was wondering whether there are any hooks that would enable me to get hold of the currently active profiles and properties, so that I could log them.
Similarly, is there a way to cause start-up to fail if the application.yml contains errors? Either that or a means for me to validate the YAML myself, so that I could kill the start-up process.
In addition to other answers: logging active properties on context refreshed event.
Java 8
package mypackage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
#Slf4j
#Component
public class AppContextEventListener {
#EventListener
public void handleContextRefreshed(ContextRefreshedEvent event) {
printActiveProperties((ConfigurableEnvironment) event.getApplicationContext().getEnvironment());
}
private void printActiveProperties(ConfigurableEnvironment env) {
System.out.println("************************* ACTIVE APP PROPERTIES ******************************");
List<MapPropertySource> propertySources = new ArrayList<>();
env.getPropertySources().forEach(it -> {
if (it instanceof MapPropertySource && it.getName().contains("applicationConfig")) {
propertySources.add((MapPropertySource) it);
}
});
propertySources.stream()
.map(propertySource -> propertySource.getSource().keySet())
.flatMap(Collection::stream)
.distinct()
.sorted()
.forEach(key -> {
try {
System.out.println(key + "=" + env.getProperty(key));
} catch (Exception e) {
log.warn("{} -> {}", key, e.getMessage());
}
});
System.out.println("******************************************************************************");
}
}
Kotlin
package mypackage
import mu.KLogging
import org.springframework.context.event.ContextRefreshedEvent
import org.springframework.context.event.EventListener
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.MapPropertySource
import org.springframework.stereotype.Component
#Component
class AppContextEventListener {
companion object : KLogging()
#EventListener
fun handleContextRefreshed(event: ContextRefreshedEvent) {
printActiveProperties(event.applicationContext.environment as ConfigurableEnvironment)
}
fun printActiveProperties(env: ConfigurableEnvironment) {
println("************************* ACTIVE APP PROPERTIES ******************************")
env.propertySources
.filter { it.name.contains("applicationConfig") }
.map { it as EnumerablePropertySource<*> }
.map { it -> it.propertyNames.toList() }
.flatMap { it }
.distinctBy { it }
.sortedBy { it }
.forEach { it ->
try {
println("$it=${env.getProperty(it)}")
} catch (e: Exception) {
logger.warn("$it -> ${e.message}")
}
}
println("******************************************************************************")
}
}
Output like:
************************* ACTIVE APP PROPERTIES ******************************
server.port=3000
spring.application.name=my-app
...
2017-12-29 13:13:32.843 WARN 36252 --- [ main] m.AppContextEventListener : spring.boot.admin.client.service-url -> Could not resolve placeholder 'management.address' in value "http://${management.address}:${server.port}"
...
spring.datasource.password=
spring.datasource.url=jdbc:postgresql://localhost/my_db?currentSchema=public
spring.datasource.username=db_user
...
******************************************************************************
I had the same problem, and wish there was a debug flag that would tell the profile processing system to spit out some useful logging. One possible way of doing it would be to register an event listener for your application context, and print out the profiles from the environment. I haven't tried doing it this way myself, so your mileage may vary. I think maybe something like what's outlined here:
How to add a hook to the application context initialization event?
Then you'd do something like this in your listener:
System.out.println("Active profiles: " + Arrays.toString(ctxt.getEnvironment().getActiveProfiles()));
Might be worth a try. Another way you could probably do it would be to declare the Environment to be injected in the code where you need to print the profiles. I.e.:
#Component
public class SomeClass {
#Autowired
private Environment env;
...
private void dumpProfiles() {
// Print whatever needed from env here
}
}
Actuator /env service displays properties, but it doesn't displays which property value is actually active. Very often you may want to override your application properties with
profile-specific application properties
command line arguments
OS environment variables
Thus you will have the same property and different values in several sources.
Snippet bellow prints active application properties values on startup:
#Configuration
public class PropertiesLogger {
private static final Logger log = LoggerFactory.getLogger(PropertiesLogger.class);
#Autowired
private AbstractEnvironment environment;
#PostConstruct
public void printProperties() {
log.info("**** APPLICATION PROPERTIES SOURCES ****");
Set<String> properties = new TreeSet<>();
for (PropertiesPropertySource p : findPropertiesPropertySources()) {
log.info(p.toString());
properties.addAll(Arrays.asList(p.getPropertyNames()));
}
log.info("**** APPLICATION PROPERTIES VALUES ****");
print(properties);
}
private List<PropertiesPropertySource> findPropertiesPropertySources() {
List<PropertiesPropertySource> propertiesPropertySources = new LinkedList<>();
for (PropertySource<?> propertySource : environment.getPropertySources()) {
if (propertySource instanceof PropertiesPropertySource) {
propertiesPropertySources.add((PropertiesPropertySource) propertySource);
}
}
return propertiesPropertySources;
}
private void print(Set<String> properties) {
for (String propertyName : properties) {
log.info("{}={}", propertyName, environment.getProperty(propertyName));
}
}
}
If application.yml contains errors it will cause a failure on startup. I guess it depends what you mean by "error" though. Certainly it will fail if the YAML is not well formed. Also if you are setting #ConfigurationProperties that are marked as ignoreInvalidFields=true for instance, or if you set a value that cannot be converted. That's a pretty wide range of errors.
The active profiles will probably be logged on startup by the Environment implementation (but in any case it's easy for you to grab that and log it in your launcher code - the toString() of teh Environment will list the active profiles I think). Active profiles (and more) are also available in the /env endpoint if you add the Actuator.
In case you want to get the active profiles before initializing the beans/application, the only way I found is registering a custom Banner in your SpringBootServletInitializer/SpringApplication (i.e. ApplicationWebXml in a JHipster application).
e.g.
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
{
// set a default to use when no profile is configured.
DefaultProfileUtil.addDefaultProfile(builder.application());
return builder.sources(MyApp.class).banner(this::printBanner);
}
/** Custom 'banner' to obtain early access to the Spring configuration to validate and debug it. */
private void printBanner(Environment env, Class<?> sourceClass, PrintStream out)
{
if (env.getProperty("spring.datasource.url") == null)
{
throw new RuntimeException(
"'spring.datasource.url' is not configured! Check your configuration files and the value of 'spring.profiles.active' in your launcher.");
}
...
}
Im currently trying to setup my own implementation of a ManagedServiceFactory. Here is what I'm trying to do: I need multiple instances of some service on a per-configuration base. With DS the components worked perfectly but now I found out that these services should handle there own lifecycle (i.e. (de)registration at the service registry) depending on the availability of some external resource, which is impossible with DS.
Thus my idea was to create a ManagedServiceFactory, which then would receive configs from the ConfigurationAdmin and create instances of my class. These again would try to connect to the resource in a seperate thread and register themselves as service when they're ready to operate.
Since I had no luck implementing this yet, I tried to break everything down to the most basic parts, not even dealing with the dynamic (de)registration, just trying to get the ManagedServiceFacotry to work:
package my.project.factory;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;
public class Factory implements BundleActivator, ManagedServiceFactory {
private ServiceRegistration myReg;
private BundleContext ctx;
private Map<String, ServiceRegistration> services;
#Override
public void start(BundleContext context) throws Exception {
System.out.println("starting factory...");
this.ctx = context;
java.util.Dictionary properties = new Hashtable<String, Object>();
properties.put(Constants.SERVICE_PID, "my.project.servicefactory");
myReg = context.registerService(ManagedServiceFactory.class, this,
properties);
System.out.println("registered as ManagedServiceFactory");
services = new HashMap<String, ServiceRegistration>();
}
#Override
public void stop(BundleContext context) throws Exception {
for(ServiceRegistration reg : services.values()) {
System.out.println("deregister " + reg);
reg.unregister();
}
if(myReg != null) {
myReg.unregister();
} else {
System.out.println("my service registration as already null " +
"(although it shouldn't)!");
}
}
#Override
public String getName() {
System.out.println("returning facotry name");
return "ServiceFactory";
}
#Override
public void updated(String pid, Dictionary properties)
throws ConfigurationException {
System.out.println("retrieved update for pid " + pid);
ServiceRegistration reg = services.get(pid);
if (reg == null) {
services.put(pid, ctx.registerService(ServiceInterface.class,
new Service(), properties));
} else {
// i should do some update here
}
}
#Override
public void deleted(String pid) {
ServiceRegistration reg = services.get(pid);
if (reg != null) {
reg.unregister();
}
}
}
Now, it should receive configurations from the ConfigurationAdmin for PID my.project.servicefactory, shouldn't it?
But it does not receive any configurations from the ConfigurationAdmin. The bundle is started, the service is registered and in the web console, I can see the config admin holds a reference to my ManagedServiceFactory. Is there a certain property which should be set? The interface specification does not suggest that. Actually my implementation is more or less the same as the example there. I've no idea what I'm doing wrong here, any pointers to the solutions are very welcome.
Also, I orginally thought to implement the ManagedServiceFactory itself as DS, which also should be possible, but I failed at the same point: no configurations are handed over by the ConfigAdmin.
update
To clarify the question: I think that this is mainly an configuration problem. As I see it, I should be able to specify two PIDs for the factory, one which identifies a configuration for the factory itself (if any), and one which would produce services trough this factory, which I thought should be the factory.pid. But the framework constants do not hold anything like this.
update 2
After searching a bit the Felix Fileinstall source code, I found out that it treats configuration files differently when there is a - in the filename or not. Having the configuration file named my.project.servicefactory.cfg it did not work, but the configs named my.project.servicefactory-foo.cfg and my.project.servicefactory-bar.cfg were properly handed over to my ManagedServiceFactory as expected, and multiple services with ServiceInterface were registered. Hurray!
update 3
As proposed by Neil, I put the declarative service part in a new question to bound the scope of this one.
I think that the problem is you have a singleton configuration record rather than a factory record. You need to call Config Admin with the createFactoryConfiguration method using my.project.servicefactory as the factoryPid.
If you are using Apache Felix FileInstall (which is a nice easy way to create config records without writing code) then you need to create a file called my.project.servicefactory-1.cfg in the load directory. You can create further configurations with the same factoryPID by calling them my.project.servicefactory-2.cfg etc.