Multiple Interface Implementations - AEM/CQ - OSGi - osgi

There is an service interface HelloService, this is implemented by 2 Service implementations
HelloService Interface
public interface HelloService {
public String getRepositoryName();
}
HelloServiceImpl1 implementation
#Service
#Component(metatype = false)
public class HelloServiceImpl1 implements HelloService {
#Reference
private SlingRepository repository;
public String getRepositoryName() {
return repository.getDescriptor(Repository.REP_NAME_DESC);
}
}
HelloServiceimpl2 implementation
#Service
#Component(metatype = false)
public class HelloServiceImpl2 implements HelloService {
public String getRepositoryName() {
return "Response from HelloServiceImpl2";
}
}
Now to use the service we use
#Reference
HelloService helloService;
Inside required method, call is made as
helloService.getRepositoryName();
I am getting response always from HelloServiceImpl1. Checked another example in AEM APIs, SlingRepository is extended by AbstractSlingRepository and AbstractSlingRepository2, how is the implementation picked internally, as while consuming we specify only #Reference SlingRepository repository;
How is this handled in AEM OSGi?
Update based on response
Checked on the syntax for this, following are observations
For using service ranking, use following with Service Implementation
#Properties({
#Property(name = Constants.SERVICE_RANKING, intValue = 100)
})
For this no change in consumption, higher service ranking implementation is picked up, control is with provider
#Reference
HelloService helloService;
For using target filter, use following annotation to specify property
#Properties({
#Property(name="type", value="Custom")
})
While consuming based on filter, specify target, control is with consumer
#Reference (target="(type=Custom)")
HelloService helloService;
If both service ranking and filter are used, filter is taking precedence.

This is related to how Declaratives Services wires a #Reference. From the specification:
If the reference has a unary cardinality and there is more than one
target service for the reference, then the bound service must be the
target service with the highest service ranking as specified by the
service.ranking property.
If there are multiple target services with
the same service ranking, then the bound service must be the target
service with the highest service ranking and the lowest service id as
specified by the service.id property.
ie, it depends of the "Service Ranking" of the component. If this ranking is not specified, then you can have any implementations (you usually get the oldest service). You can use a filter if you want to target a specific implementation.

Related

how to use org.springframework.format.Formatter.print()

#Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
#Override
public FormattingConversionService mvcConversionService() {
FormattingConversionService f = super.mvcConversionService();
f.addFormatter(new DateFormatter("yyyy-MM-dd"));
return f;
}
}
#RestController
public class TestController {
#GetMapping
public Date test(Date date) {
return date;
}
}
When we access http://localhost:8080?date=2021-09-04, the argument type is converted through the DateFormatter's parse method, which relies on the SpringMVC framework to do the conversion. I wonder if the print method can also be invoked through the framework to return a string.
Do we need to manually invoke the print method, for example
#RestController
public class TestController {
#Resource
private FormattingConversionService conversionService;
#GetMapping
public String test(Date date) {
return conversionService.convert(date, String.class);
}
}
Inside the controller
You could use a class extending java.text.Format like SimpleDateFormatin your controller:
#RestController
public class TestController {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
#GetMapping
public String test(Date date) {
return dateFormat.format(date);
}
}
At application level
Use DateTimeFormatterRegistrar to register your formats, like described in this tutorial.
Then you can register this set of formatters at Spring's FormattingConversionService.
Using Jackson
However if you would like to work with JSON or XML you should consider using FasterXML's Jackson. See similar question:
Spring 3.2 Date time format
This is the interface representing the environment in which the current application is running. It models two key aspects of the application environment: profiles and properties. The methods related to property access are exposed via the PropertyResolver superinterface.
A profile is a named, logical group of bean definitions to be registered with the container only if the given profile is active. Beans may be assigned to a profile whether defined in XML or via annotations; see the spring-beans 3.1 schema or the #Profile annotation for syntax details. The role of the Environment object with relation to profiles is in determining which profiles (if any) are currently active, and which profiles (if any) should be active by default.
Properties play an important role in almost all applications, and may originate from a variety of sources: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps, and so on. The role of the environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them.
Beans managed within an ApplicationContext may register to be EnvironmentAware or #Inject the Environment in order to query profile state or resolve properties directly.
In most cases, however, application-level beans should not need to interact with the Environment directly but instead may have to have ${...} property values replaced by a property placeholder configurer such as PropertySourcesPlaceholderConfigurer, which itself is EnvironmentAware and as of Spring 3.1 is registered by default when using context:property-placeholder/.
Configuration of the environment object must be done through the ConfigurableEnvironment interface, returned from all AbstractApplicationContext subclass getEnvironment() methods. See ConfigurableEnvironment Javadoc for usage examples demonstrating manipulation of property sources prior to application context refresh().

How to use #Autowired in an class annotated with #Entity?

I have an entity called TimeBooking. When I request this entity and return to the client I want to get a list of ActivityTimeBookings from a repository. But when the function get called the repo is null.
So I tried to #Autowired the repo and marked it as transient and also said Spring that there is a dependency which should be injected.
#Configurable(preConstruction = true)
#Entity
public class TimeBooking extends BaseEntity{
#Autowired
private transient ActivityTimeBookingRepository activityTimeBookingRepository;
...
#JsonProperty("activityTimeBookings")
private List<ActivityTimeBooking> activityTimeBookings() {
return this.activityTimeBookingRepository.findByDate(this.timeFrom);
}
}
Any suggestions?
Using #Autowired in a class annotated with #Entity is a bad practice.
The solution is given below :
1. Create a service interface :
public interface TimeBookingService {
public List<ActivityTimeBooking> activityTimeBookings();
}
2. Create an implementation of the service interface :
#Service
public class TimeBookingServiceImpl implements TimeBookingService {
#Autowired
private ActivityTimeBookingRepository activityTimeBookingRepository;
public List<ActivityTimeBooking> activityTimeBookings() {
return this.activityTimeBookingRepository.findByDate(this.timeFrom);
}
}
Usually its indeed a bad practice to inject something into JPA entities.
These are usually created by JPA implementation (like Hibernate) and spring as a DI framework doesn't really participate in this process.
Note, that there can be many instances of this class created as a result of query, so if you later use this for serialization of the list of this object you might end up running N queries to the database given N entities like this were retrieved.
Answering your question about "getting access to the repo" I believe you should consider refactoring:
In the service class (assuming you have a "regular" contoller, service and dao):
you can:
class MyService {
SomeResult.. doSomething() {
List<TimeBooking> allTimeBookings = dao.getAllTimeBooking();
LocalDateTime timeFrom = calculateTimeFrom(allTimeBookings);
List<ActivityTimeBooking> allActivityTimeBookings = dao.findByDate(timeFrom);
return calculateResults(allTimeBookings, allActivityTimeBooking);
}
}
class MyDao {
List<ActivityTimeBooking> findByDate(LocalDateTime timeFrom) {...}
List<TimeBooking> getAllTimeBookings() {...}
}
Regarding the service implementation, I've assumed this use case can't be covered by usual "JOIN between two tables" so that that creating an association between TimeBooking and ActivityTimeBooking is not an option.
Note 2, I've used one repository (dao) for brevity, in real application you might want to inject two different repositories into the service.

How to choose bean implementation at runtime for every http request

I am having two implementations of my component.
public interface MyComponent {
}
imple1
#Component("impCompf")
#Lazy
#RequestScope
public class ImpComp1 implements MyComponent {
}
imple2
#Component("impComps")
#Lazy
#RequestScope
public class ImpComp2 implements MyComponent {
}
What I did so far is to create two conditions like so:
imple1
public class FirstCondition implements Condition {
#Override
public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
return staticVariable.contains("impCompf");
}
}
Same goes for imple2
and define a configuration class
#Configuration
public class MyConfiguration {
#Bean
#Conditional(FirstCondition .class)
#Primary
public MyComponent getComp1() {
return new ImpComp1();
}
public static String staticVariable= "impCompf";
and in My main controller:
#RequestMapping(value="api/{co}", method=RequestMethod.POST)
public ResponseEntity<Modelx> postSe(#PathVariable("co") String co) {
if(co.contains("impCompf"))
staticVariable = "impCompf";
else (co.contains("impComps"))
staticVariable = "impComps";
What I want: for every http request I want to load proper implementation
But however what I am getting is the implementation defined first in the static variable.
If is there another elegant and better way, i'd like to know about it.
I think there is some confusion here about the purpose of the conditions. These aren't being used at the time your requests arrive to autowire the candidate bean into your controller. These are being used when the application is started to configure the application context based on the environment and classpath etc...
There is no need for the conditional classes that you have created. This is defining the configuration of the beans when the context starts and not on a per request basis at runtime.
The use of the static variable is also problematic is a scenario with one or more concurrent requests or in a case where multiple threads may observe different values unless some other mechanism in the java memory model is being used (such as volatile or establishing a happens before relationship, e.g. with sychnronized)
There are a number of ways to do what you appear to be trying to achieve. Since ultimately, you appear to be using a path parameter supplied by a client to determine which service you want to invoke you could use a classic factory pattern to return the correct interface implementation based on the string input programmatically.
Alternatively you could create two distinct controller methods which are distinguished by a query parameter or endpoint name or path match etc. You could then have the appropriate service injected by a qualified bean name
Although perhaps generally recommended, you could also inject an application context instance and search the it looking for the relevant bean by name or class: https://brunozambiazi.wordpress.com/2016/01/16/getting-spring-beans-programmatically/ - although This is more cumbersome and you'd need to handle things like org.springframework.beans.factory.NoSuchBeanDefinitionException or casting in some cases - best avoided in favour of one of the other methods.

Spring boot rest ignore one class

I am developing a REST API using spring-boot-starter-data-rest. One class I want to sync with JPA is the User class containing information about users, including who is allowed to access the API.
Unfortunately, having the User and the UserRepository means that my User class is exposed in my API. I was able to remove things like the Id (in the configureRepositoryRestConfiguration function) and usernames and passwords (by adding #JsonIgnore to every variable of my User class).
Unfortunately, users of the API can still ask for the users table (who returns a list with empty users). Although this is not really a problem, I would rather remove the /users endpoint.
Adding #JsonIgnore to the whole User class is not possible.
Exporting repositories is depend on RepositoryDetectionStrategy. The default strategy is:
Exposes all public repository interfaces but considers #(Repository)RestResource’s exported flag.
According it to disable exporting of your 'repo' you can set exported flag to false for this repo:
#RepositoryRestResource(exported = false)
public interface UserRepo extends JpaRepository<User, Integer> {
//...
}
Another approach is to change globally the RepositoryDetectionStrategy to ANNOTATED:
Only repositories annotated with #(Repository)RestResource are exposed, unless their exported flag is set to false.
#Configuration
public class RestConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setRepositoryDetectionStrategy(RepositoryDetectionStrategy.RepositoryDetectionStrategies.ANNOTATED);
super.configureRepositoryRestConfiguration(config);
}
}
Then don't apply #RepositoryRestResource annotation to repos that doesn't need to be exported.
UPDATE
We can also use this application property to setup the strategy:
spring.data.rest.detection-strategy=default
Source
You can hide certain repositories by adding this annotation to your repository: #RepositoryRestResource(exported = false).
More informations here: http://docs.spring.io/spring-data/rest/docs/current/reference/html/#customizing-sdr.hiding-repositories
There's such thing as projections.
You can define interface with fields you want and use it as repository's method:
#Projection(name = "simpleUser", types = { User.class })
interface SimpleUser {
String getFirstName();
String getLastName();
}

OSGi - How Can OSGi - I create a component that register a different implementation based on a parameter? Factory?

I've the following bundles:
- GreetingAPI (bundle which defines the greeting() method) (Service)
- GreetingImpl1 (bundle which implements greeting() method for English mode)
- GreetingImpl2 (bundle which implements greeting() method for Italian mode)
- GreetingConsumer (bundle which uses the greeting service)
How Can I create a component (I suppose it's a factory) that based on a given language parameter lets the consumer bundle to use a different implementation of the service.
You're thinking about this the wrong way around. The provider should not register a different service depending on something that the consumer does, because the provider shouldn't know anything about the consumer.
Instead, you can have multiple providers of the same service but annotate them with appropriate metadata. Then the consumer of the service can choose whether or not to filter on specific properties.
For example, when we register a service we can add properties as follows (note that I am using the OSGi Declarative Services annotations, see OSGi Compendium Release 5, section 112.8):
#Component(property = "locale=en_GB")
public class MyGreetingImpl1 implements Greeting {
public String greet() { return "How do you do"; }
}
#Component(property = "locale=en_US")
public class MyGreetingImpl2 implements Greeting {
public String greet() { return "Howdy"; }
}
#Component(property = "locale=fr_FR")
public class MyGreetingImpl3 implements Greeting {
public String greet() { return "Bonjour"; }
}
Now the consumer can choose whichever language it wants using a target filter. Note the use of a wildcard, as the consumer in this case only cares about the language but not the country code:
#Component
public class GreetingConsumer {
#Reference(target = "(language=en*)")
public void setGreeting(Greeting greeting) { ... }
}
One of the possible solutions is to have some kind of a language manager. So your consumer has a language manager and not directly the greetings service
The manager is notified with the registration / deregistration of every language implementation of your GreetingAPI.
Your language manager keeps trace of the diffrent implementations. Your manager provides the right implementation of the target language (using an enum for example)
Example
public class LanguageManagerImpl implements LanguageManager {
//LanguageEnum can be used to distinguish the different languages
private Map<LanguageEnum, GreetingAPI> greetings = new HashMap<LanguageEnum, GreetingAPI>();
public void registerLanguage(GreetingAPI greeting) {
LanguageEnum language = greeting.getLanguageEnum();
//add the greetings to the map
}
public void deregisterLanguage(GreetingAPI greeting){
//remove your greeting from the map
}
public GreetingAPI getGreetingForLanguage(LanguageEnum language) {
return greetings.get(language);
}
}
If you are using blueprint, then you need to add to the language manager blueprint a reference-list with a refence listener on GreetingAPI
Otherwise you use traditional listeners
Example
<bean id="languageManager"
class="your.language.managerimpl.LanguageManagerImpl">
</bean>
<service ref="languageManager" interface="your.language.manager.interface.LanguageManager" />
<reference-list interface="your.greeting.interface.GreetingAPI"
availability="optional">
<reference-listener ref="languageManager"
bind-method="registerLanguage" unbind-method="deregisterLanguage" />
</reference-list>

Resources