I was writting one service and observed #Property we can define also inside the service as in my below code :
#Component(metatype = true, immediate = true, description = "Demo Service to test")
#Service(value = DemoService.class)
#Properties({
#Property(name = "testprop" , value = "This is Test Property")
})
public class DemoServiceImpl implements DemoService {
#Property(name = "localprop", value = "Local Value")
#Activate
public void activate(ComponentContext ctx)
{
String testprop = (String)ctx.getProperties().get("testprop");
String localprop = (String)ctx.getProperties().get("localprop");
}
}
Both properties display in felix console & accessible inside my service. So what creates the difference to declare them inside component or outside. One i saw we can not use #Properties inside component. But not sure what makes them functionally different from each other & when to use each.
I guess you talk about the felix annotations.
You can use #Property in front of any method or member variable. However, that does not mean that the method would be called or the variable would be set. The only good thing in it is that if you have a specific variable that will hold the value of the property (by assigning it in the activate method), your class can be more self explaining based on the annotations.
In the other hand, you can list properties within the #Properties annotation. I prefer this way as in this case I can define exactly the order of properties how they should appear in the generated metatype xml file (and on the webconsole).
It can also happen, that the configuration property is not assigned to any member variable, it is used only in the activate method. In that case the best place to define it is within the #Properties annotation in front of the class.
Related
In a legacy code, I'm working with, I found the following thing:
#Autowired
final lateinit var controller: CustomController
what does this final keyword mean here?
In a Kotlin documentation I found a short description about final keyword that is blocking overriding of the methods in open classes but no information about fields. Also - the class within which I found the line is not open
A final property or a method in Kotlin prevents overriding of the field / method. That being said, Kotlin by default considers a property or a method/function to be final unless specified by the keyword open. In your case, the final keyword is redundant.
Here's a small demo test case to illustrate the same.
open class Parent {
open val someValue = 0
final val otherValue = 13 // redundant modifier 'final' warning in Android Studio
}
class Child : Parent() {
override val someValue = 5
// override val otherValue = 19 // compile error
}
There is an interesting problem called Fragile Base Class in OOP and why some languages like Kotlin prefer final by default.
What you have there is a property, not a field.
It looks just like a field, as it would in Java; but in Kotlin, it actually defines a public getter method, a public setter method, and a private backing field*.
So the final modifier applies to the accessor methods, preventing those from being overridden in a subclass. (As you say, the backing field itself can't be overridden anyway.)
As Siddharth says, final is the default in Kotlin, so you usually wouldn't need to specify it, though there are a few situations in which it would be needed — e.g. if it were already overriding something, or you were using the all-open or kotlin-spring compiler plug-ins. (The use of #Autowired suggests that this is a Spring module, which probably explains why final is needed here.) In any case, your IDE would probably indicate where it's not needed, e.g. by showing it greyed-out.
(* Only the getter is necessary; the setter isn't generated for a val, and the backing field isn't generated if you override the accessor(s) and they don't refer to it.)
I want to initialise a member variable from a value I pick from ENV but it is not available in init block as it gets picked up after object initialisation
private lateinit var needValueHere: String
#Value("\${CLIENT_ID:NA}")
private val CLIENT_ID: String = ""
init {
this.needValueHere = this.CLIENT_ID
}
This is a simplified version of the actual problem.
I have verified the value is available in the member functions.
Your object is constructing by the following way:
Create object (e.g. call constructor)
Via reflection: put dependencies (e.g. fill values under #Autowired, #Value and other annotations).
Your init block is part of constructor, e.g. all Spring-related items aren't initialized here.
How you can fix this:
Extract properties to the type-safe configuration (please see official docs here)
Use notation of class like below.
Create private lateinit var field and don't call it until Spring initialization finishing (this is useful for integration tests, e.g. test methods start only after full warmup). Another option - use kotlin lazy notation. However whole this item couldn't be named as "good code".
class MyService(#Value("\${CLIENT_ID:NA}") private val needValueHere: String) {
/* */
}
I am trying to find a way to do the following in my spring boot 1.5 application.
I have a variable who's value is dynamic meaning it comes in from an external system.
String name = "abc"; //gets set externally
I want to try and use the name's value to lookup my property file and see if there is a matching property defined. something like..
#Value("#{myClassName.name.concat('something')}")
String propertyValue;
Now my application.property file has the following property set
assume name has the value "abc"
property file contents:
abc.something:abcValue
Now, when i try to access the value of the variable propertyValue it gets set to the value abc.something and not abcValue.
I probably think I cannot use #Value with #{} to get to that, I was wondering if there was a way to to use #{} inside ${} so that I goes and fetches the property value after calculating the name of the property using #{}.
Let me know if you need more details please.
A bean life-cycle requires properties to be resolved at compile time. So, #Value requires constant parameter.
You can use Environment bean to access your properties programmatically.
import org.springframework.core.env.Environment;
#Service
public class Serivce {
#Autowired
private Environment environment;
public String getProperty(final String keyPart) {
String key = "build.your." + keyPart;
return environment.getProperty(key)
}
}
By the way you can use #('${spring.some.property}') in SpEL to access placeholder.
// This is valid access to property
#Value("#('${spring.some.property}')")
private String property;
Is it possible to run multiple instance of the same service in the osgi framework?
More specific, I need to start multiple instances of a service, but each instance should recieve different parameters. This is because the services have similar functionality. But instead of writing a service for every variation, I want to reuse one implementing class.
I've already found the registerService method in the framework api.
ServiceRegistration<?> registration = bundlecontext.registerService(
className, class, null);
however, i seem to create only one instance of each class. Is there a workaround for this?
preferably something like
ServiceRegistration<?> registration = bundlecontext.registerService(
className + "#" + (++counter), new classInstance(), null);
Note that using Declarative Services with the corresponding annotations makes this quite easy, here's an excerpt from the Apache Sling codebase (ConfiguredFeature.java):
#Component(
name = "org.apache.sling.featureflags.Feature",
metatype = true,
configurationFactory = true,
policy = ConfigurationPolicy.REQUIRE)
#Service
public class ConfiguredFeature implements Feature {
#Property(label = "Name", description = "Short name of this feature")
private static final String NAME = "name";
private String name;
#Activate
private void activate(final Map<String, Object> configuration) {
this.name = PropertiesUtil.toString(configuration.get(NAME), "");
}
...
}
Using configurationFactory = true and policy = ConfigurationPolicy.REQUIRE causes one instance of this service to be created for each corresponding OSGi configuration, which is a natural way of creating multiple instances.
You could create a ManagedServiceFactory. The factory can register a new service for each configuration set in Configuration Admin.
I have a simple example here which uses Felix DependencyManager to register the component: https://github.com/paulbakker/osgicourse/tree/master/greeterfactory/src/greeterfactory
You have the parameters slightly wrong, or at least misleading:
ServiceRegistration<?> registration = bundlecontext.registerService(
className, class, null);
The second parameter to registerService is an object, not a class. This is an object you instantiate yourself. You can create as many as you like, in whatever way you like, before passing them to OSGi.
However if you are doing this with externally-supplied configuration data, should look into Declarative Services and their ability to receive config from the OSGi Configuration Admin service.
UPDATE
Taking another look at your question, I see the counter that you tried to add to the class name. This is not required and in fact not permitted either. Just call registerService multiple times.
I'm trying to add ConfigSlurper's ConfigObjectinto an application context Environment in order to provide configuration values to the context.
The MapPropertySorce itself only expects its values to be of type Object only. But in the end property resolution fails as the EnvironmentAccessor will try to cast each ConfigObject to String.
So basically the question is, is there support for non String property resource values? Any supporting classes there (different EnvironmentAccessor?)
class ConfigSlurperLearningSpec extends Specification {
def configurationResource = new ClassPathResource("/META-INF/de.troi/application.configuration")
ConfigObject configuration = new ConfigSlurper().parse(configurationResource.getURL())
def "use as application context property source"() {
expect:
AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext()
context.register(PropertySourceInjection)
context.getEnvironment().getPropertySources().addLast(new MapPropertySource("mapConfiguration", configuration))
context.refresh()
String configuredValue=context.getBean('testBean')
configuredValue=='create'
}
}
#Configuration
class PropertySourceInjection {
#Value("#{environment['entityManagerFactory']['jpaPropertyMap']['hibernate.hbm2ddl.auto']}")
Object hibernateHbm2ddlAuto;
#Bean
String testBean() {
return new String(hibernateHbm2ddlAuto.toString())
}
}
It is definitely possible to inject non-string objects via ConfigSlurper.
Take a look at (shameless plug) https://github.com/ctzen/slurper-configuration