I am working on a project with two possible deployment environment, selected using #Alternative (or more specifically, #Stereotype) annotation. Let's call them envDefault and envAlt.
I am looking for a way to define a bean that has an #Alternative defined for envAlt, but has no #Default implementation for envDefault. I think this is viable, as the bean is not injected in any of common implementations, and actions that cause its creation (it #Observes specific event) will not happen in envDefault. Yet CDI fails to start the applicaiton, due to typical "Unsatisfied dependencies with qualifier #Default" exception at WeldStartService validation.
Is there a way to relax CDI validation for this specific bean to allow envDefault deployment without #Default implementation?
EDIT:
For reference, as it was already answered:
interface AltOnlyInterface {}
#Alternative
class AltOnlyBean implements AltOnlyInterface {}
//no default implementation of AltOnlyInterface
interface OtherInterface {}
//AltOnlyInterface is not use in default environment
class RegularOtherBean implements OtherInterface {}
#Alternative
class AltOtherBean implements OtherInterface {
#Inject
AltOnlyInterface altOnlyBean;
}
If there is no default implementation you should:
create that implementation
or
make all injection points as Instance
If you want to have two environments and only in one has that bean. You need to annotate bean with #Alternative and enable it in one environtment. In the second environtment it will fail unless you have Instance at injection points.
Related
I'm trying to define a configuration to create an ExecutionGraphQlService to wire into an existing application as a proof of concept, but I'm a bit confused about how to create an instance of a AnnotatedControllerConfigurer. Here is what I currently have settled upon.
AnnotatedControllerConfigurer annotatedControllerConfigurer = new AnnotatedControllerConfigurer();
annotatedControllerConfigurer.setApplicationContext(applicationContext);
annotatedControllerConfigurer.afterPropertiesSet();
annotatedControllerConfigurer.configure(runtimeWiringBuilder);
AnnotatedControllerConfigurer implements ApplicationContextAware, InitializingBean so it seems to expect to be initialized as a bean, but attempts to autowire an instance of it fail due to Could not autowire. No beans of 'AnnotatedControllerConfigurer' type found.
I've attempted constructor injection public ExecutionGraphQlService defaultExecutionGraphQlService(AnnotatedControllerConfigurer annotatedControllerConfigurer) as well as manual instantiation autowireCapableBeanFactory.autowireBean(annotatedControllerConfigurer) (which should be basically the same thing).
It seems like this class is not a bean, but expects to be managed by Spring's bean lifecycle? Is my approach the expected approach?
The AnnotatedControllerConfigurer type is meant to be a bean in the Spring container, and is using the bean lifecycle to setup the infrastructure.
Creating it as a bean (given other pieces are contributed as beans as well). The runtime wiring should be configured on the GraphQlSource directly.
#Configuration(proxyBeanMethods = false)
class GraphQlConfiguration {
#Bean
public AnnotatedControllerConfigurer annotatedControllerConfigurer() {
return new AnnotatedControllerConfigurer();
}
}
I want to override a bean that's used by Quarkus to disable authentication/authorization.
With the following implementation, it works that REST endpoints can be configured at start time to not be secured:
#Alternative
#Priority(1)
#ApplicationScoped
public class CustomOidcAuthController extends TestAuthController {
private static final Logger LOGGER = Logger.getLogger(CustomOidcAuthController.class);
#ConfigProperty(name = "quarkus.oidc.enabled", defaultValue = "false")
boolean quarkusOidcEnabled;
#PostConstruct
public void check() {
LOGGER.info("isAuthorizationEnabled(): " + isAuthorizationEnabled());
}
#Override
public boolean isAuthorizationEnabled() {
return quarkusOidcEnabled;
}
}
This is with the Bean residing in the same Quarkus module.
However, I want to externalize this class into a separate library and if I do this, it no longer works.
Noteworthy:
Yes, the #Priority of my bean (1) is higher than the default (3000)
The beans are discovered, if I explicitly inject them.
They are however not used, if I inject the subtype that Quarkus uses internally (either TestAuthController or AuthorizationController).
Therefore the endpoints are always secured
As can be seen here from the IntelliJ debugger
Currently I have an empty beans.xml, but with building a Jandex Index I also observe the same behavior (related How to create a Jandex index in Quarkus for classes in a external module)
I can get the behavior I want, if I use quarkus.arc.selected-alternatives=com.xxx.CustomOidcAuthController, however this is not preferable, since each Service using the library would need to configure this and this will certainly cause problems, because it can be easily forgotten.
Well, the priority of the TestAuthController is indeed 3000 and therefore it takes precedence. Injection of CustomOidcAuthController works because there's no other bean that has CustomOidcAuthController in its set of bean types.
In other words, it works as expected (and defined by the spec).
Yes, the #Priority of my bean (1) is higher than the default (3000)
According to CDI specification, an alternative with highest priority is selected. See this part of the CDI 2.0 specification.
Here is a CDI TCK test asserting that higher priority 'wins' during typesafe resolution.
Therefore, your approach is correct and you just need to make sure your custom bean is an alternative with priority value higher than that of TestAuthController.
Using Weld 1.1.13.Final in test with Arquillian....
Let's say I inject into a field something volatile. Something like a property subject to change that I want the bean owning the injection point to receive change events. Thought about creating a CDI extension.
Caught ProcessAnnotatedType event and looking for all fields that have an custom annotation on field injection points:
<T> void pat(#Observes ProcessAnnotatedType<T> event, BeanManager bm) {
final AnnotatedType<T> target = event.getAnnotatedType();
for (AnnotatedField<? super T> field : target.getFields())
if (field.isAnnotationPresent(Value.class)) { // ignore that I don't check #Inject here for the moment
CtClass wrapper = pool.get(target.getJavaClass().getName());
ConstPool cp = wrapper.getClassFile().getConstPool();
CtMethod m = CtNewMethod.make(....)
....
wrapper.addMethod(m);
event.setAnnotatedType(bm.createAnnotatedType(wrapper.toClass()));
}
}
Had even grabbed thereafter all the injection points for fields and replaced the underlying WeldField with a new Field corresponding the "wrapper" type. Otherwise bean validation fails.
But this only works for stuff setup during startup not when for example Arquillian uses the Bean Manager to initialize a class that injects one of my "wraps". Things fail since the Bean Resolver uses the Type as a hash key to find beans.
Basically I don't think I can "mask" a class that is annotated (made into a bean) by the CDI with an extra method to receive custom events. Would have been cool but a Type is a Type (i.e. no idea how to proxy or fake the equals/hashCode).
Got it. Turns out the compute value function (google extension) inside the TypeSafeBeanResolver resolver (at least the CDI Weld implementation) is smart. If I just extend the class:
CtClass wrapper = pool.makeClass(target.getJavaClass().getName()+"Proxy");
wrapper.setSuperclass(pool.get(target.getJavaClass().getName()));
.....
final AnnotatedType<T> other = bm.createAnnotatedType(wrapper
.toClass());
then everything works fine. Tested capturing an event in a bean. Will post the code on a Gist with a comment.
For some reasons, I would like to deploy my application as two separate artifacts: Users-ejb.jar and Users-war.war, not packaged in the same ear (but still, deployed in the same JBoss AS 7.1 instance). In the Users-war.war I have a backing bean (annotated as a JSF managed bean) where I wish to inject an EJB3 packaged in the Users-ejb.jar. The simple #EJB injection that worked when everything was packaged in a single ear no longer works when the Users-ejb.jar and the Users-war.war are deployed seperately.
A narrowed-down simplified example of my setup follows:
EJB3 bean
import javax.ejb.*;
(...)
#Stateless(name="userFacade")
#Local(IUserFacadeLocal.class)
#Remote(IUserFacadeRemote.class)
public class UserFacade extends AbstractFacade<User> implements IUserFacadeLocal, IUserFacadeRemote {
Backing bean
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.ejb.EJB;
import entities.User;
import facades.IUserFacadeRemote;
import facades.IUserFacadeLocal;
#ManagedBean(name="indexBackingBean")
#SessionScoped
public class IndexBackingBean implements Serializable {
#EJB(beanName="userFacade")
private IUserFacadeLocal userFacade;
I've tried various combinations like declaring the type of the EJB3 bean in the backing bean as IUserFacadeRemote (as opposed to IUserFacadeLocal) but they all fail with the same exception when the Users-war.war module is deployed:
Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException:
JBAS014543: No EJB found with interface of type 'facades.IUserFacadeLocal' and
name 'userFacade' for binding controllers.IndexBackingBean/userFacade
The Users-ejb.jar is deployed to JBoss AS 7.1 without any complains but when the Users-war.war is deployed, JBoss complains that it can't find the bean he's supposed to inject.
However, I am able to obtain a reference to the EJB3 bean using JNDI using:
String jndiName = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote";
this.userFacade = (IUserFacadeRemote) new InitialContext().lookup(jndiName);
Despite that, the #EJB injection doesn't seem to work.
UPDATE:
I followed the suggestion give below by Tom Anderson and the injection that does work is the:
#EJB(mappedName = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote")
which if I understand correctly uses the vendor-specific mappedName attribute. I couldn't get the injection to work in a vendor-independent way.
I wish i understood this area of the EE spec well enough to give you a definitive answer, but i don't.
The JBoss EJB documentation has this to say:
The #EJB annotation also has a mappedName() attribute. The specification leaves this a vendor specific metadata, but JBoss recognizes mappedName() as the global JNDI name of the EJB you are referencing. If you have specified a mappedName(), then all other attributes are ignored and this global JNDI name is used for binding.
If you specify #EJB with no attributes defined [...] Then the following rules apply:
The EJB jar of the referencing bean is searched for an EJB with the interface, used in for #EJB injection. If there are more than one EJB that publishes same business interface, then an exception is thrown. If there is only one bean with that interface then that one is used.
Search the EAR for EJBs that publish that interface. If there are duplicates, then an exception is thrown. Otherwise the matching bean is returned.
Search globally in JBoss for an EJB of that interface. Again, if duplicates, an exception is thrown.
#EJB.beanName() corresponds to . If the beanName() is defined, then use the same algorithm as #EJB with no attributes defined except use the beanName() as a key in the search. An exception to this rule is if you use the ejb-link '#' syntax. The '#' syntax allows you to put a relative path to a jar in the EAR where the EJB you are referencing lives. See spec for more details
The "Search globally in JBoss for an EJB of that interface" certainly suggests that an injection like the one you wrote should work. Indeed, that it should work without the beanName. However, my suspicion is that from the point of view of a component in the WAR, a component in the EJB-JAR is remote, and therefore you will need to use the remote interface.
So, the first thing i'd try is:
#EJB
private IUserFacadeRemote userFacade;
Without a beanName, in case that's making trouble. It sounds like you've tried that, though.
If the normal approach to injection doesn't work, i might fall back to trying an injection via a mappedName, which in JBoss is a global JNDI name. So:
#EJB(mappedName = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote")
private IUserFacadeRemote userFacade;
This is obviously rather ugly.
Anyway, good luck!
EDIT: Something else you could try is to use a qualified relative beanName which explicitly names the EJB-JAR:
#EJB(beanName = "Users-ejb.jar#userFacade")
private IUserFacadeRemote userFacade;
Because the WAR and EJB-JAR are not packaged in an EAR, this might need to be:
#EJB(beanName = "../Users-ejb.jar#userFacade")
private IUserFacadeRemote userFacade;
But by this point i'm just guessing.
EDIT STRIKES BACK: We may have overlooked something very simple. The lookup attribute of the #EJB annotation lets you specify "A portable lookup string containing the JNDI name for the target EJB component", hence:
#EJB(lookup = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote")
private IUserFacadeRemote userFacade;
Might work. This is essentially a portable version of the JBoss-specific use of mappedName.
I have been testing this scenario in Wildfly and found that it will work with local interfaces as described above if there is a jboss-deployment-structure.xml inside of the war pointing to the ejb. Otherwise a ClassNotFoundException is thrown as the war above can't really "know" about the ejbs classes due to the modular class loading in JBoss and Wildfly. The content of the file should be:
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="deployment.Users-ejb.jar" />
</dependencies>
</deployment>
</jboss-deployment-structure>
And then the JSF bean can use:
#EJB(lookup = "java:global/Users-ejb/userFacade!facades.IUserFacadeRemote")
private IUserFacadeLocal userFacade;
As #TomAnderson said, the standard way to achieve cross-artifact lookup is the lookup attribute of the #EJB annotation.
Here's a complete Maven project to illustrate how this works:
https://github.com/mrts/remote-ejb-injection
You don't need to use the name attribute of the EJB class, providing the class name in lookup is sufficient. Quoting from the example above:
// in API JAR
#Remote
public interface HelloService { ... }
// in EJB JAR
#Stateless
public class HelloServiceImpl implements HelloService { ... }
// in WAR
#WebServlet("/hello")
public class HelloServlet extends HttpServlet {
#EJB(lookup = "java:global/service-ear/service-ejb-impl/HelloServiceImpl!" +
"ee.mrts.service.HelloService")
private HelloService helloService;
...
}
(So, using HelloServiceImpl directly in lookup Just Works™.)
Is it possible to explicitly deny JSF from serializing some component trees? At the moment I am passing a non-serializable object to a <h:inputText>:
<h:inputText value="#{nonSerializableBean.nonSerializableClassInstance}" />
What happens after a few clicks is that I get (during view restoration):
javax.faces.FacesException: Unexpected error restoring state for component
with id configurationForm:j_idt292:j_idt302:field. Cause:
java.lang.IllegalStateException: java.lang.InstantiationException:
my.namespace.NonSerializableClass
I think this occurs because JSF cannot restore the nonSerializableClassInstance:
Caused by: java.lang.IllegalStateException: java.lang.InstantiationException: com.foobar.utils.text.Period
at javax.faces.component.StateHolderSaver.restore(StateHolderSaver.java:110)
at javax.faces.component.ComponentStateHelper.restoreState(ComponentStateHelper.java:292)
at javax.faces.component.UIComponentBase.restoreState(UIComponentBase.java:1444)
at javax.faces.component.UIOutput.restoreState(UIOutput.java:255)
at javax.faces.component.UIInput.restoreState(UIInput.java:1359)
A bonus question: Is it ok not to make backing beans Serializable? Should this then prevent serialization/deserialization of these?
Some background:
We have a bunch of 3rd party classes that we need to provide forms for in JSF. The problem is that we cannot directly use these classes on JSF pages, because they do not implement Serializable interface, and thus will/should fail if JSF runtime decides to serialize/deserialize the page and the component-tree. The classes are "closed" and we are not allowed to modify them.
Running Mojarra 2.0.2.
Javabeans are by spec supposed to implement Serializable. JSF just follows/adheres this spec.
The classes are "closed" and we are not allowed to modify them.
Your best bet is to wrap it as a transient property of a class which implements Serializable and implement the writeObject() and readObject() accordingly.
public class SerializableClass implements Serializable {
private transient NonSerializableClass nonSerializableClass;
public SerializableClass(NonSerializableClass nonSerializableClass) {
this.nonSerializableClass = nonSerializableClass;
}
public NonSerializableClass getNonSerializableClass() {
return nonSerializableClass;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(nonSerializableClass.getProperty1());
oos.writeObject(nonSerializableClass.getProperty2());
// ...
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
nonSerializableClass = new NonSerializableClass();
nonSerializableClass.setProperty1((String) ois.readObject());
nonSerializableClass.setProperty2((String) ois.readObject());
// ...
}
}
Finally use that class instead. You could eventually let it extends NonSerializableClass and then autogenerate delegate methods by a bit decent IDE.
Either way, it's only going to be a lot of opaque and boilerplate code, but since you're not allowed to modify those classes... (I would personally just push that 3rd party stuff to have them their so-called Javabeans to implement Serializable since it are them who's breaking the standards/specs).
I don't know what you expect if the class members (e.g. nonSerializableClassInstance) are not getting serialized.
Of course, you can mark them as transient.
The aim of a managed bean is to hold the application state - you will lose the state if some members are not getting serialized (if the server has the need of doing this).