i am going to continue to express my seemingly endless missunderstanding in EJBs:
I am using JBoss 8 (Wildfly 8.1) as my Applicationserver.
I am currently building a workaround for the Problem, that Inpustreams cannot be passed to remote EJB via RMI (which makes absolute sense once i thought about it).
I i must not replace the existing Resources Interface (writing an Inpustream to a File) I build following 3 Projects as a solution:
Interfaces (library included in both EARs):
com.package.ejb
public interface StorageAdapter extends Serializable{
String store(Inpustream is); (//returns an id
Inputstream load(String id);
}
public interface StorageAdapterProvider{
StorageAdapter provide();
}
Persistence-EAR
com.package.impl
public class FileSystemStorageAdapter implements com.package.ejb.Storageadapter {
//implementation, writing to locally mounted path in filesystem...
}
com.package.impl
#Singleton
#Remote(com.package.ejb.StorageAdapterProvider.class)
public class StorageAdapterProviderBean implements com.package.ejb.StorageadapterProvider {
public StorageAdapter provide() {
return new FileSystemStorageAdapter();
}
}
Business-EAR
com.package.business
public class StorageProvider {
#EJB(looklup = "java:global/Persistence-EAR/StorageAdapterProviderBean!com.package.ejb.StorageAdapterProvider"
private StorageAdapterProvider provider;
#Produces
public StorageAdapter getStorageAdapter() {
return provider.provide();
}
}
I then use #Inject StorageAdapter storageAdapter; to get an instance of the Storageadapter-implementation.
Business-EAR/Storageprovider then throws a ClassCastException, telling me that com.package.impl.FileSystemStorageAdapter cannot be cast to com.package.ejb.StorageAdapter
i added some logging to the EJB
com.package.impl
#Singleton
#Remote(com.package.ejb.StorageAdapterProvider.class)
public class StorageAdapterProviderBean implements com.package.ejb.StorageadapterProvider {
public StorageAdapter provide() {
StroageAdapter ret = new FileSystemStorageAdapter();
logger.info("EJB: RETURNING Stortage adapter");
logger.info(" is of type:"+ret.getClass().getName());
logger.info(" is Storageadapter: "+ (ret instanceof StorageAdapter));
logger.info(" is FileSystemStorageAdapter: "+ (ret instanceof FileSystemStorageAdapter));
return ret;
}
}
and it correctly prints:
EJB: RETURNING Stortage adapter
is of type:com.package.impl.FileSystemStorageAdapter
is Storageadapter: true
is FileSystemStorageAdapter: true
Further investigation shows, that the Exception is thrown while the container is Wrapping the "ret" Object, as i also get the Exeption if i chnage the #Provides implementation to:
Object o = provider.provide();
Is it impossible to return an Interface type from an EJB?
What am i missing?
Does Business-EJB need to know the implementing Classes? - Wouldn't a ClassNotFound Exception make more sense in this case?
Thanks in advance!
EDIT:
My Packaging looks as follows:
Persistence.ear
-lib/Interfaces.jar
-StorageAdapter
-Storageprovider
-persistence_ejb.jar
-FileSystemStorageAdapter
-StorageAdapterProviderBean
Business.ear
-lib/Interfaces.jar
-StorageAdapter
-Storageprovider
-business_ejb.jar
-StorageProvider
-web.war
-Jaxrsres
The Jaxrsres is the one having a Storageadapter Injected via #Inject
I am deploying these 2 wars to a JBoss Wildlfly 8.1 Server.
I did not make any relevant changes to the configuration - so all the other Point you requested are defaults as far as I understand.
EDIT2:
It somewhat defeats the purpose, but it works if i add the Persistence.ear/persistence_ejb.jar to the libraries of Business.ear.
Business.ear
-lib
-Interfaces.jar
-StorageAdapter
-Storageprovider
-persistence_ejb.jar
-FileSystemStorageAdapter
-StorageAdapterProviderBean
-business_ejb.jar
-StorageProvider
-web.war
-Jaxrsres
This is obviously not what I want though :/
The main Point is, i want to be able to redeploy Persistence.ear with, say, "DatabaseStorageAdapter", without touching business.ear.
The "Dream" is to Provide the Storageadapter as an EJB Directly - but as the Interface is fixed and needs an InputStream, this cannot be done.
Related
I want to build a JEE plugin based architecture. The main idea is do something similar to what eclipse is, but in the context of JEE.
My goal is to have a minimum of modules as the core, and allow others modules extend its functionality.
To this, I have implemented a test using 4 modules:
gauges: Defines and implements a gaugesregistry service, also defines a gauge POJO.
cashgauges: implements a gauge producer using CDI. this is a plugin mock.
othergauges: implements a gauge producer using CDI. this is a second plugin mock.
gauges-web: Contains a basic JSF view to query the gauges registry.
dependencies are as follows:
cashgauges --> gauges
othergauges --> gauges
gauges-web --> gauges
This is done by using jboss-deployment-structure.xml on each deployed file.
The deployment is done as individual files:
gauges.jar
cashgauges.jar
othergauges.jar
gauges-web.war
All services start, but what I see is, my gaugesregistry is instantiated several times. I started wildfly in debug mode and what I see is each module has its own instance of gaugesregistry: cashgauges and othergauges call same method (addGauge) on registry, but instances of this registry are not the same.
This happens in both cases, using #ApplicationScoped and #Singleton annotations. What am I doing wrong?
Source code is available on https://github.com/hatit/research
After a couple of days, I'm considering using a ServiceLocator pattern and remote references instead of CDI. Any suggestions?
Great, i got twice -2 votes (-4 reputation) because i asked an advanced topic for software developers?
I searched in about stackoverflow and found this
Founded in 2008, Stack Overflow is the largest, most trusted online community for developers to learn, share their knowledge, and build their careers...
If any interested in this topic, then:
After some hours understanding differences between CDI Beans and EJBs lifecycle when used as independent modules (JBoss Modules), i found:
Singleton CDI Beans are instantiated one time per module, not really singleton among all modules.
To avoid this i had to create Registry as a Singleton Enterprise Session Bean.
This cames with new problems, CDI injection doesn't works among modules, so i had to package a CDI producer (i don't care if it's singleton or not, its only a producer) which can be instantiated by any module. Main responsibility of this producer is to lookup Registry EJB, this to avoid hardcoding jndi path each time i need access the Registry.
I changed my trivial example to support JSF plugins also, this is an example of what i am using currently.
Module facelets:
Registry interface:
public interface FaceletsModuleRegistry {
void registerModule(String module);
List<String> getRegisteredModules();
}
Registry implementation:
#Local(FaceletsModuleRegistry.class)
#Singleton(name="FaceletsModuleRegistry")
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
#Vetoed
public class FaceletsModuleRegistryImpl implements FaceletsModuleRegistry {
private Set<String> registeredModuleNames = new TreeSet<>();
#Override
public void registerModule(String module) {
registeredModuleNames.add(module);
}
#Override
public List<String> getRegisteredModules() {
return Collections.unmodifiableList(new ArrayList<>(registeredModuleNames));
}
}
Registry producer:
#ApplicationScoped
public class FaceletsModuleRegistryBuilder {
#EJB(lookup="java:global/facelets/FaceletsModuleRegistry!co.hatit.enterprise.facelets.services.FaceletsModuleRegistry")
protected FaceletsModuleRegistry faceletsModuleRegistry;
#Produces
public FaceletsModuleRegistry getFaceletsModuleRegistry(){
return faceletsModuleRegistry;
}
}
Any other module that i want to plugin implements this code (please see #Inject can be used on any module requiring access the Registry singleton instance):
#ApplicationScoped
public class InmueblesActivator {
#Inject
private FaceletsModuleRegistry faceletsModuleRegistry;
public void init(#Observes #Initialized(ApplicationScoped.class) Object init){
String moduleName = Module.getCallerModule().getIdentifier().getName();
String name = StringUtils.substringBetween(moduleName, "deployment.", ".jar");
faceletsModuleRegistry.registerModule(name);
}
}
Then i can reference Registry from any module as a really singleton instance (solved my problem having multiple instances of same class when used CDI singleton beans among several modules).
Now, i can plugin JEE modules, not just java code, but facelets resources also:
public class FaceletsResourceHandler extends ResourceHandlerWrapper {
Logger logger = LoggerFactory.getLogger(FaceletsResourceHandler.class);
#Inject
FaceletsModuleRegistry faceletsModuleRegistry;
private ResourceHandler wrapped;
public FaceletsResourceHandler(ResourceHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ViewResource createViewResource(FacesContext context, final String name) {
ViewResource resource = super.createViewResource(context, name);
if (resource == null) {
resource = new ViewResource() {
#Override
public URL getURL() {
try {
//iterates over plugins to find the required resource.
for(String module : faceletsModuleRegistry.getRegisteredModules()){
URL resource = Module.getCallerModule().getModuleLoader()
.loadModule(ModuleIdentifier.create("deployment." + module + ".jar"))
.getExportedResource("META-INF/resources" + name);
if (resource != null) return resource;
}
} catch (ModuleLoadException e) {
throw new FacesException(e);
}
return null;
}
};
}
return resource;
}
#Override
public ResourceHandler getWrapped() {
return wrapped;
}
}
We have jax.rs rest application build on Resteasy version 3.1.0.Beta2.
Via extending Application class we add our rest resources and also othre filtres and providers.
One of them is also ResteasyJacksonProvider
#ApplicationPath("")
public class ApplicationConfig extends Application {
final private Set<Class<?>> resources = new HashSet<>();
public ApplicationConfig() {
this.resources.add(ResteasyJacksonProvider.class);
this.resources.add(UserProfileResource.class);
this.resources.add(StatsResource.class);
}
#Override
public Set<Class<?>> getClasses() {
return Collections.unmodifiableSet(this.resources);
}
}
Everythings works fine till I extend it and replace with my custom provider.
#Provider
#Consumes({"application/*+json", "text/json"})
#Produces({"application/*+json", "text/json"})
public class SecureJacksonProvider extends ResteasyJacksonProvider {
}
But now I am getting strange exception for one of the rest calls:
Direct self-reference leading to cycle (through reference chain...
So question is: If there is some reference cycle why it works with default ResteasyJacksonProvider but not with its child.
Note:
I have tried using also JacksonJsonProvider which is parent of ResteasyJacksonProvider and it also thows that exception. - It leads me to think if there can be some problem with dependencies but I found nothing.
I'm using Guice in a Restlet web server, and there's one pattern I can't figure out: how to inject objects that are specific to a certain user or a certain request.
Say we have a request to list all the Widgets that belong to a project. The service that looks up Widgets requires a Project instance. There are many Projects in the system.
My code currently looks something like this:
public class WidgetResource extends ServerResource {
//path: project/{project}/widgets
private final WidgetService service;
private final ProjectLookup projectLookup;
#Inject
public WidgetResource(WidgetService service, ProjectLookup projectLookup) {
this.service = service;
this.projectLookup = projectLookup;
}
#Get
public WidgetCollection getWidgets() {
String projectName = getAttribute("project"); //restlet lookup of path var
Project project = projectLookup.get(projectName);
WidgetCollection widgets = service.getWidgetsFor(project);
return widgets;
}
}
This works well enough, but it's clumsy, and I hope there's a better way. It would be great to inject the correct Project object directly. Is there a way to do this?
So far I've explored AssistedInject, which gives a factory object very similar to my Lookup. I came close to an answer with custom annotations/injections, but dead-ended because the Restlet attributes map isn't populated until after injection. Have read the GitHub docs and the User's Guide. Can't spot anything.
I'd like to end up with something like this:
public class WidgetResource extends ServerResource {
private final WidgetService service;
#Inject
public WidgetResource(WidgetService service) {
this.service = service;
}
#Inject
#Get
public WidgetCollection getWidgets(#PathName("project") Project project) {
WidgetCollection widgets = service.getWidgetsFor(project);
return widgets;
}
with (of course) a #Provides method in the configuration that would look up the path variable and use the lookup. However, I can't figure out a way to hand a provider method the path name or the Resource instance as variables. Is this possible? Any help appreciated!
To your vision: You can not inject into "getWidgets" ... injection happens the very moment your widgetResource is created, so basically once your application/server starts.
Besides that, it looks perfectly fine. You have a REST resource that takes a project parameter and uses a service to look up widgets.
If you know all possible project names in advance, you could use guice' mapBinder instead of the service.
public class WidgetsModule extends AbstractModule {
protected void configure() {
MapBinder<String, WidgetCollection> mapbinder
= MapBinder.newMapBinder(binder(), String.class, WidgetCollection.class);
mapbinder.addBinding("project1").toInstance(...);
mapbinder.addBinding("project2").toProvider(...);
mapbinder.addBinding("project3").to(...);
}
}
With GWT you have stuff like this:
public interface LoginConstants extends Constants {
#DefaultStringValue("Wellcome to my super app")
#Key("appDescription")
String appDescription();
#DefaultStringValue("Ok")
#Key("okButtonLabel")
String okButtonLabel();
}
Then you can use from your classes doing GWT.create(LoginConstant.class), in this way the interface is backed by dynamic implementation that, when I call loginConstants.appDescription() returns the value contained from a property file using the #Key annotation to reference the key in the property file. If the property file misses the property, then de #DefaultStringValue is returned. This is used for internationalization, but can possibly work also for configuration.
But with GWT, this is meant to be used on the client side (ie. translated to JavaScript), and for i18n, not for configuration.
But, I find this idea very convenient also for configuration handling.
I wonder if somebody knows a framework to do a similar thing on the server side, without necessarily bind your code to GWT. ie. if there is any library that implements this kind of logic specifically designed for the configuration handling. I am not aware of anything like this.
Reference to the feature in GWT: https://developers.google.com/web-toolkit/doc/latest/DevGuideI18nConstants
I implemented my own solution to the question:
BASIC USAGE
The approach used by OWNER APIs, is to define a Java interface
associated to a properties file.
Suppose your properties file is defined as ServerConfig.properties:
port=80
hostname=foobar.com
maxThreads=100
To access this property you need to define a convenient Java interface
in ServerConfig.java:
public interface ServerConfig extends Config {
int port();
String hostname();
int maxThreads();
}
We'll call this interface the Properties Mapping Interface or just
Mapping Interface since its goal is to map Properties into an easy to
use a piece of code.
Then, you can use it from inside your code:
public class MyApp {
public static void main(String[] args) {
ServerConfig cfg = ConfigFactory.create(ServerConfig.class);
System.out.println("Server " + cfg.hostname() + ":" + cfg.port() +
" will run " + cfg.maxThreads());
}
}
But this is just the tip of the iceberg.
Continue reading here: Basic usage || Website || Github
I still have a couple of features in mind, but the current implementation goes a little forward than the basic functionalities described in the questions.
I need to add samples and documentation.
I loved the idea so much that I quickly assembled some code using Java Dynamic proxies.
So basically you create an interface with relevant methods and annotate them with #Key, #DefaultStringValue annotations.
Below is the sample Java code:
Main.java
package net.viralpatel;
import net.viralpatel.annotations.DefaultStringValue;
import net.viralpatel.annotations.Key;
interface LoginConstants extends Constants {
#DefaultStringValue("Wellcome to my super app")
#Key("appDescription")
String appDescription();
#DefaultStringValue("Ok")
#Key("okButtonLabel")
String okButtonLabel();
}
public class Main {
public static void main(String[] args) {
LoginConstants constants = DynamicProperty.create(LoginConstants.class);
System.out.println(constants.appDescription());
System.out.println(constants.okButtonLabel());
}
}
Also the property file in background that we load is
config.property
okButtonLabel=This is OK
Just execute the Main java class, following output will be displayed:
Output:
Wellcome to my super app
This is OK
Here is the rest of code: http://viralpatel.net/blogs/dynamic-property-loader-using-java-dynamic-proxy-pattern/
You could mimic that with spring (but I'm not sure it's worth it):
#Component
public class SomeBean {
#Value("${appDescription:Wellcome to my super app}")
private String appDescription;
#Value("${okButtonLabel:Ok}")
private String okButtonLabel;
// accessors
}
with a PropertyPlaceHolderConfigurer.
I would like to consider the CDI as the following :-
The Qualifier
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.METHOD,
ElementType.FIELD,
ElementType.PARAMETER,
ElementType.TYPE
})
#Documented
public #interface MessageTemplate {
#Nonbinding
String baseName();
#Nonbinding
Locale locale() default Locale.ENGLISH;
#Nonbinding
String key();
}
The Producer
public class CustomizedProducer {
#Produces
#MessageTemplate(baseName = "",
key = "")
public String createMessageTemplate(final InjectionPoint ip) {
MessageTemplate configure = null;
ResourceBundle bundle = null;
try{
configure = ip.getAnnotated().getAnnotation(MessageTemplate.class);
bundle = ResourceBundle.getBundle(configure.baseName(),
configure.locale());
return bundle.getString(configure.key());
} finally{
configure = null;
bundle = null;
}
}
}
The Service Configure
public class MyServiceConfigure {
#Inject
#MessageTemplate(baseName = "com.my.domain.MyProp",
key = "appDescription")
private String appDescription;
#Inject
#MessageTemplate(baseName = "com.my.domain.MyProp",
key = "okButtonLabel")
private String okButtonLabel;
//Getter
}
The working class
public class MyService {
#Inject
private MyServiceConfigure configure;
public void doSomething() {
System.out.println(configure.getAppDescription());
System.out.println(configure.getOkButtonLabel());
}
}
Regarding to the coding above you may use the java.util.Properties instead of the java.util.ResourceBundle and provide the default member to the Qualifier as well.
If you are running these under the JavaEE 6, the CDI is already enable for you. Just put the empty beans.xml to the META-INF or WEB-INF. If you are running under the Java SE you may need a bit further work as mentioned at the Weld web site and its documentation.
I'm using the CDI as a main part of my current production project and it works quite well.
EDITED:-
The good point to use the CDI is the Scope, we may produce the #MessageTemplate as the #ApplicationScope,#SessionScoped, #RequestScoped, #ConversationScoped or the pseudo-scope as #Singleton or #Depenendent
If you annotate the MyServiceConfigure as #Named, it is ready to use at the JSF as well.
I have the following EJB class instantiated in an application running in JBoss 5
public interface ISlaveServer {
public String getId();
public String getName();
}
#Remote
public interface IMasterServer {
public String getId();
public void addSlave(ISlaveServer slaveServer);
public void removeSlave(ISlaveServer slaveServer);
}
#Stateless
#RemoteBinding(jndiBinding = "MasterServer")
public class MasterServer implements IMasterServer, Serializable {
UUID id = UUID.randomUUID().toString();
public String getId() { return id.toString(); }
public void addSlave(ISlaveServer slaveServer) { ... }
public void removeSlave(ISlaveServer slaveServer) { ... }
}
I Have the following class instantiated in an application running in Tomcat 6
public static class SlaveServer implements ISlaveServer, Serializable {
UUID id = UUID.randomUUID().toString();
public String getId() { return id.toString(); }
public String getName() { return "SlaveServer"; }
}
Finally I have the following code also running in the Tomcat based application...
Properties properties = new Properties();
properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs", "org.jboss.naming.client");
properties.put("java.naming.provider.url", "jnp://localhost:1099");
try {
Context ctx = new InitialContext(properties);
IMasterServer masterServer = (IMasterServer) ctx.lookup("MasterServer");
String masterId = masterServer.getId();
masterServer.addVideoServer(new SlaveServer());
}
catch(NamingException e) {
e.printStackTrace();
}
Everything is working fine up until the call to
masterServer.addVideoServer(new SlaveServer());
at which time I get the following exception...
java.lang.ClassNotFoundException: org.test.SlaveServerTest$SlaveServer (no security manager: RMI class loader disabled)
From what I can tell this exception might be originating from the remote JBoss server because the remote calls are working ( masterServer.getId() works fine ). Just the call where I am passing a locally implemented object is failing.
What do I need to do to get this working?
The SlaveServer class is Serializable. This means that this class must be made available to both the client (the JNDI snippet) and the server (MasterServer). When a class cannot be found on the server, RMI has the capability of downloading code from a remote location. However, executing code downloaded from a remote client is a potentially dangerous operation, this is only allowed if a security manager has been installed.
You'll need to either include the SlaveServer class in the application containing MasterServer (or some server class path), or you'll need to stop using Serializable.
The "static" was there because the original SlaveServer class was a nested class.
I have moved the class to the top-level (thus removing the static) and it is still a no go; I get the same exception.
It seems to me I need to do something like CORBA's "activation" to my SlaveServer. That way the JBoss server should be able to get the stubs for my SlaveServer inside Tomcat.
/Edit
There is no implementation of ISlaveServer in the JBoss application. I want it to pass a "remote reference" from the Tomcat application to the JBoss application so it shouldn't really be serializing it anyway (just a reference to it).
To get this working I needed to implement the ISlaveServer interface as an RMI remote interface.
public interface ISlaveServer extends java.rmi.Remote {
...
}
and make sure that the SlaveServer class was a proper RemoteObject...
public class SlaveServer extends java.rmi.RemoteObject implements ISlaveServer {
}
Finally I had to make sure the SlaveServer was exported through RMI before using it...
static SlaveServer slaveServer = new SlaveServer();
Properties properties = new Properties();
properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs", "org.jboss.naming.client");
properties.put("java.naming.provider.url", "jnp://localhost:1099");
try {
Context ctx = new InitialContext(properties);
IMasterServer masterServer = (IMasterServer) ctx.lookup("MasterServer");
String masterId = masterServer.getId();
masterServer.addVideoServer((ISlaveServer)UnicastRemoteObject.exportObject(slaveServer, 0));
}
catch(NamingException e) {
e.printStackTrace();
}
This correctly communicates with the Remote EJB and passes a reference to my SlaveServer object which the EJB can use to communicate back with the calling VM.
FYI SlaveServer is static because in RMI it is your responsibility to hold on to a reference to the real object since RMI only holds weak-references. If you don't you will get "Object not in table" errors from RMI.