I'm trying to play with OSGi and I stuck with one problem...
I have simple class FirstVersionPrinter
public class FirstVersionPrinter implements VersionPrinter {
private final static String VERSION = "111";
#Override
public String printVersion() {
return VERSION;
}
}
and interface
public interface VersionPrinter {
public String printVersion();
}
and Locator class
#Override
public String print(Integer version) {
final BundleContext bundleContext = FrameworkUtil.getBundle(getClass()).getBundleContext();
for (Bundle bundle : bundleContext.getBundles()) {
BundleContext bundleContext1 = bundle.getBundleContext();
ServiceReference sr = bundleContext1.getServiceReference(VersionPrinter.class.getName());
...
(1) ((VersionPrinter) bundleContext1.getService(sr)).printVersion());
in Locator I'm trying to iterate through bundles and execute printVersion for my VersionPrinter. But in line (1) I got ClassCastException
java.lang.ClassCastException: example.FirstVersionPrinter cannot be cast to example.VersionPrinter
Could someone clarify why it's happens?
Thanks.
PS: Locator and FirstVersionPrinter in the different bundles
This always happens if more than one bundle contains the package example. If the "same" class is loaded by two different class loaders then it is not considered to be the same by java.
So make sure you ideally have the interface class only in one bundle and export it from there. All bundles that need the class should import this package.
If for some reason you have to package the example package in more than one bundle make sure that all those bundles import and export the package in their Manifest. This makes sure that OSGi will select only one of the packages for export and import this from all other bundles.
Btw. the way you iterate through the bundles and ask each BundleContext for a service reference is not correct. You only need to use one BundleContext and do context.getServiceReferences to get all you FirstVersionPrinter services.
Related
I started developing my own dependency injection package in dart just for fun. The problem I came across is that when a class is in it's own file and is not imported anywhere, dart's tree shaking will remove it and make it inaccessible. This makes it impossible for me to map implementation of some interface to the interface so I cannot ask context to give me implementation when I supply the interface. Here is what I mean:
import 'context/context.dart';
void main() {
Context context = Context();
Interface interface = context.getInstance<Interface>();
interface.works();
}
abstract class Interface {
void works();
}
And here is the implementation of the "Interface" abstract class:
#Singleton()
class Implementation implements Interface {
#override
void works() {
print('works');
}
}
If I import the file that contains the "Implementation" class, context is able to find it and map it but I want to avoid doing that. Is there any way to turn off the tree shaking or preserve the class without importing it ?
I'm writing a GraphQL api in java. I would like to provide this GraphQL api in a JAR file that this api implementation could be consumed/reused in other Java EE applications running in OpenLiberty 22. This is my api implementation.
import org.eclipse.microprofile.graphql.Description;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Name;
import org.eclipse.microprofile.graphql.NonNull;
import org.eclipse.microprofile.graphql.Query;
...
#GraphQLApi
#RequestScoped
public class SystemStatusGraphQL {
#Inject
private DbAdapter databaseAdapter;
#Query("system")
#NonNull
#Description("Gets status information about the system")
public SystemStatus getSystemStatus(#Name("name") String name) {
return database.getCurrentStatus(name);
}
}
I deployed this JAR file as maven package and consumed it in my target application and I have two seperate problems or questions now.
How to reuse this API so that #GraphQLApi is recognized by OpenLiberty? I tried to inherit from the api class but OpenLiberty does not load GraphQL endpoint.
public class MySystemStatusGraphQL extends com.test.stystem.status.api.SystemStatusGraphQL {
}
When I paste all the GraphQL stuff provided at the top and only try to reuse the model class SystemStatus, Jandex can not resolve object types. When starting OpenLibertyServer, this error occurs: Class [com.mylib.system.status.database.model.SystemStatus] is not indexed in Jandex. Can not scan Object Type, might not be mapped correctly. This error retains even if I create jandex index file on build and add it as part of the JAR file. The SystemStatus class contains all type definitions as you can see:
#Type("SystemStatus")
#Description("Describes current state of system.")
public class SystemStatus{
#NonNull
#Name("_id")
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#NonNull
#Name("serial")
private String serial;
public String getSerial() {
return this.serial;
}
}
I would prefer to reuse whole api which brings the issue mentioned in question 1. If this is not possible, how can I solve issue mentioned in question 2?
I'm currently working on a Quarkus extension which is basically a filter that is using a PanacheMongoRepository. Here is a code snippet (this is in the runtime part of the extension) :
#Provider
#Priority(Priorities.AUTHORIZATION)
#AuthorizationSecured
public class AuthorizationFilter implements ContainerRequestFilter {
// Some injection here
#Inject
UserRepository userRepository;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Some business logic here...
UserEntity userEntity = userRepository.findByName(name);
// Some business logic here...
}
}
The repository :
#ApplicationScoped
public class UserRepository implements PanacheMongoRepository<UserEntity> {
public UserEntity findByName(String name) {
return find("some query...", name).firstResult();
}
}
When the repository is called, I get the following exception:
org.jboss.resteasy.spi.UnhandledException: java.lang.IllegalStateException: This method is normally automatically overridden in subclasses...
java.lang.IllegalStateException: This method is normally automatically overridden in subclasses\n\tat io.quarkus.mongodb.panache.common.runtime.MongoOperations.implementationInjectionMissing(MongoOperations.java:765)\n\tat io.quarkus.mongodb.panache.PanacheMongoRepositoryBase.find(PanacheMongoRepositoryBase.java:119)
The processor
class AuthorizeProcessor {
private static final String FEATURE = "authorize";
#BuildStep
FeatureBuildItem feature() {
return new FeatureBuildItem(FEATURE);
}
#BuildStep(onlyIf = IsAuthorizeEnabled.class)
void registerAuthorizeFilter(
BuildProducer<AdditionalBeanBuildItem> additionalBeanProducer,
BuildProducer<ResteasyJaxrsProviderBuildItem> resteasyJaxrsProviderProducer
) {
additionalBeanProducer.produce(new AdditionalBeanBuildItem(UserRepository.class));
additionalBeanProducer.produce(new AdditionalBeanBuildItem(AuthorizationFilter.class));
resteasyJaxrsProviderProducer.produce(new ResteasyJaxrsProviderBuildItem(AuthorizationFilter.class.getName()));
}
}
Any idea ?
Thanks for your help :)
MongoDB with Panache (and the same for Hibernate with Panache) uses bytecode enhancement at build time. When this enhancement didn't occurs it leads to the exception you mentionned at runtime: java.lang.IllegalStateException: This method is normally automatically overridden in subclasses
It can occurs only when the repository or entity is not in the Jandex index. Jandex is used to index all the code of your application to avoid using reflection and classpath scanning to discover classes. If your entity / repository is not in the index this means it's not part of your application as we automatically index the classes of your application, so it must be inside an external JAR.
Usually, this is solved by adding the Jandex plugin to index the code of the external JAR (in fact there is multiple way to do this, see How to Generate a Jandex Index).
An extension suffer from the same issue as extensions are not indexed by default. But from an extension you can index the needed classes via a build step wich is more easy and avoid polluting the index with classes that are not needed.
This can be done by generating a new AdditionalIndexedClassesBuildItem(UserRepository.class.getName()) inside a build step.
I am trying to create a very basic sling service in AEM:
package com.mypackage;
/**
* A simple service interface
*/
public interface TestService {
/**
* #return the name of the underlying JCR repository implementation
*/
public String getPropertyName();
}
The implementation class:
package com.mymypackage.impl;
import javax.jcr.Repository;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.jcr.api.SlingRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mypackage.TestService;
#Component(label = "Title", description = "Description.", immediate = true, metatype = true, policy = ConfigurationPolicy.REQUIRE)
#Service(value = {TestService.class})
#Properties({
#Property(name = "propertyPath", label = "Property Label", description = "Property Desc.")
})
public class TestServiceImpl implements TestService {
private static final Logger log = LoggerFactory.getLogger(TestServiceImpl.class);
String propertyPath = null;
#Activate
public void activate(ComponentContext ctx) {
Dictionary properties = ctx.getProperties();
String propertyPath =(String)properties.get("propertyPath");
log.info("====================getPropertyName activate========================"+propertyPath);
this.propertyPath = propertyPath;
}
#Override
public String getPropertyName() {
// TODO Auto-generated method stub
log.info("====================getPropertyName========================"+propertyPath);
return propertyPath;
}
}
and I have created a node of type sling:OsgiConfig inside the config folder. The name of this node is com.mypackage.impl.TestServiceImpl.xml and are the content of it:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
propertyPath="http://www.myuhc.com"/>
and this is how I am trying to use it inside a Java class:
public static String getTestService() {
TestService testService = new TestServiceImpl();
String prop = testService.getPropertyName();
return prop;
}
This method is being called from a JSP using customtaglib (method being mapped through a .tld file)
When I use this approach the activate method inside the HelloServiceImpl class is not called and hence the property is not set. But when I use the service inside a component JSP like this:
<%#page import="org.osgi.service.cm.ConfigurationAdmin"%>
<%#page import="org.osgi.service.cm.Configuration"%>
<%
Configuration conf = sling.getService(org.osgi.service.cm.ConfigurationAdmin.class).getConfiguration("Name of the config");
String myProp = (String) conf.getProperties().get("property key");
%>
everything works fine. There is something really wrong I must be doing while trying to call the service from a Java class. How can I use that approach. I don't want to use scriptlet inside JSP. Also, any best practices would be highly appreciated.
Thanks in advance
OSGi Services work within a life cycle which manages the creation and deletion of the Service instances.
As part of the container management of these instances, when the container creates the instance it calls the activation method with the appropriate values. So that the property can be assigned.
What you are doing in your first code snippet:
TestService testService = new TestServiceImpl();
String prop = testService.getPropertyName();
Is not using the containers version of your component. Your using the direct implementation and bypassing the container management.
To use the instance that is managed by the container. You need to request it from the container.
Which is exactly what your second snippet shows
sling.getService(org.osgi.service.cm.ConfigurationAdmin.class)
Is requesting the best matching service from the container.
To access a service from the container. You either need to be a Service yourself. Which you can do by the #Reference annotation.
You mentioned however being in a taglib which makes things a bit more complicated. You need to obtain a reference to the SlingScriptHelper which can be obtained from the pageContext like this;
ServletRequest request = pageContext.getRequest();
final SlingBindings bindings = (SlingBindings) request
.getAttribute(SlingBindings.class.getName());
final SlingScriptHelper scriptHelper = bindings.getSling();
TestService service= scriptHelper
.getService(com.mymypackage.impl.TestService.class);
The problem is, that sometimes when you try to re-install a bundle, some old compiled classess are not removed and replaced with a new one. Try to remove the /var/classes and/or /var/clientlibs nodes and re-install your projects.
This may also be useful, if you are unable to use #Reference annotation for injecting your service
// get the TestServiceImpl.java service component
TestServiceImpl testService = getSlingScriptHelper().getService(TestServiceImpl.class);
//com.adobe.cq.sightly.WCMUsePojo.getSlingScriptHelper()
I am looking at this example: Building Applications in JavaFX 2.0 and they show a custom SpringFxmlLoader:
import java.io.IOException;
import java.io.InputStream;
import javafx.fxml.FXMLLoader;
import org.springframework.context.ApplicationContext;
public class SpringFxmlLoader
{
private ApplicationContext context;
public SpringFxmlLoader(ApplicationContext context)
{
this.context = context;
}
public Object load(String url, Class<?> controllerClass) throws IOException
{
InputStream fxmlStream = null;
try
{
fxmlStream = controllerClass.getResourceAsStream(url);
Object instance = context.getBean(controllerClass);
FXMLLoader loader = new FXMLLoader();
loader.getNamespace().put("controller", instance);
return loader.load(fxmlStream);
}
finally
{
if (fxmlStream != null)
{
fxmlStream.close();
}
}
}
}`
Why does one need to create a specific spring FXML Loader? I mean, even with a simple fxml loader, when you load a fxml like this:
AnchorPane page = (AnchorPane) FXMLLoader.load(TabePaneGraph.class.getResource("Sample.fxml")); the Sample Controller is called anyways and any initialization is still done. I am trying to understand the motivation behind this specific custom SpringFxmlLoader implementation.
There are multiple ways to skin a cat. I'm guessing the motivation to use spring in that article is because many web developers would be familiar with it. It also might make it look more like an alternative to a Java EE application. Which it is, but not because you can use spring with it.
You don't need any dependency injection framework to develop with JavaFX, in fact we need to look carefully at our dependencies because they will add to download time if you are expecting users to download your application.
There are (at least) 2 ways, how to specify a controller:
declare the controller class in the FXML file: note that you specify
the class not the instance here. The FXMLoader will create a new
instance.
pass an existing instance (e.g. "this" or as here a bean instantiated with Spring) as the controller to the FXMLLoader
loader.getNamespace().put("controller", instance);
I'm not sure about this part, but I think it can be replaced with setController() in the latest JavaFX version.