Calling EJB in JBoss from Tomcat & passing an object as an argument - java

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.

Related

SpringBoot pass local variable

I'm creating a Java application that depending on certain conditions/configurations it instantiates a SpringBoot application to receive some HTTP messages.
My problem is that I have a ReceiverService that needs to use some variables that are created outside of the SpringBoot application. Is there a way to pass local java variables (from inside the application, not outside like the shell or files) to SpringBoot components?
Example:
I have a Manager object that, depending on some conditions, it defines a variable param that I want to use in the SpringBoot Component ReceiverService.
public class Manager {
public Manager(bool condition) {
String param = "foo";
if (condition) {
param = "bar";
}
ReceiverApp receiver = new ReceiverApp(); // init SpringBoot app
}
}
The SpringBoot app:
#SpringBootApplication
public class ReceiverApp {
public ReceiverApp() {
SpringApplication.run(ReceiverApp.class);
}
#Component
public class ReceiverService implements InitializingBean {
final CustomObject obj1 = new CustomObject(param);
#Override
public void aFunction() throws Exception {
MyConfig config = MyConfig.build(param);
}
}
SpringApplication.run is designed to accept key=value pairs like the ones you give in command line parameters to the main method in Java.
Since you seem to have the other Spring boot application jar in your class-path, and seem to be able to just instanciate the ReceiverApp, you could just pass the parameters as strings (of the format String[]{"key1=value1", "key2=value2"})
These can be passed to SpringApplication.run, and these will automatically become Spring configuration values, which can be injected anywhere in the application.
#SpringBootApplication
public class ReceiverApp {
public ReceiverApp(String[] notReallyFromCommandLineArgs) {
SpringApplication.run(ReceiverApp.class, notReallyFromCommandLineArgs);
}
You can send the parameters like this:
String[] params = new String[]{"my.params.param1="+param1};
ReceiverApp receiver = new ReceiverApp(params);
You can inject them anywhere in that Receiver Spring application as a value.
#Value("my.params.param1")
String theParam1;
Refer:
https://stackoverflow.com/a/55890457/1364747
There are several ways to create a manager app which configures another app.
Scenario 1
A common way is to put the config in a database which is accessed directly by both apps, although this is less clean (it creates a hard dependency via the database).
Scenario 2
A cleaner solution is if the manager app owns the database, and offers a config service which is consumed (and presumably cached) by the configured app. Typically the config service would be accessed via remoting, e.g. REST, so the two apps can be deployed and run independently.
Scenario 3
If you want to keep it simple, you could reverse the setup and implement the config service in the configured app itself (sort of like the management interface in Tomcat). Then you could either add a web UI in the same app and be done with it, or build a separate standalone client which interacts with that service. In this last two scenarios, think about security because you might not want any client to be able to connect and change the config.
You can have ReceiverService inject a Supplier<String> (or define your own interface if you prefer a less generic naming) and make Manager implement it.
#Component
public class ReceiverService implements InitializingBean {
#Autowired
private Supplier<String> paramSupplier;
final CustomObject obj1 = new CustomObject(param);
#Override
public void aFunction() throws Exception {
MyConfig config = MyConfig.build(paramSupplier.get());
}
}
public class Manager implements Supplier<String> {
private final String param;
public String get() { return param; }
public Manager(bool condition) {
param = "foo";
if (condition) {
param = "bar";
}
// why is this in the Manager constructor???
ReceiverApp receiver = new ReceiverApp(); // init SpringBoot app
}
}
Or you define your own #Component to supply the parameter and have both manager and receiver inject it.

Google Guice Properties Management

I would like to create a proper properties management strategy in a java webapp that relays on google guice as a DI framework.
I would like to have a mechanism answering the following 3 requirements:
I would like to be able to inject properties using guice (#Named)
I would like to be able to access properties in a static way
The mechanism should support prioritization of properties, meaning that a property can be wrapped in the deployed war with a certain value but it can also be redundant in the target system level or local file system (of the target machine I deploy on), in such a case the value in the war will be overridden by the value that exists in the target machine.
I believe this is a standard requirement. Now, using guice standard binder I can easily get the first requirement but not the other two. To get the other two I created my own class that does the following:
Wraps and exposes the binding methods of guice (those that binds properties) For example:
public static void bindString(AnnotatedBindingBuilder<String> binder, String property, String defaultValue) {
binder.annotatedWith(Names.named(property)).toInstance(getProperty(property, defaultValue));
}
Where the getProperty method knows how to handle my properties (get the value from the war or system level) and exposes the properties statically as well.
So basically as long as I'm using this utility that I created for properties bindings I'm good, it covers all my requirements but once I use the standard guice bindings I'm losing the second and third requirement.
Is there a way to override guice bindings and get all those 3 requirements?
Once I had the same challange in a spring based app and was pretty easy. I implemented ApplicationContextInitializer with the following method:
#Override
public void initialize(ConfigurableWebApplicationContext ctx) {
PropertySource<Map<String, Object>> localProps = null;
try {
localProps = new ResourcePropertySource(new ClassPathResource(LOCAL_PROPERTIES_FILE_NAME));
} catch (IOException e) {
LOG.fatal("Could not load local properties from classpath " + LOCAL_PROPERTIES_FILE_NAME);
return;
}
LOG.info("Loaded configuration from classpath local file " + LOCAL_PROPERTIES_FILE_NAME);
ctx.getEnvironment().getPropertySources().addFirst(localProps);
}
so this gave me a way to add local properties with highest priority to my Environment. In case of overlap with war properties the local ones had higher priority. In addition I exposed my Environment statically so I has static access to my properties (for services that are not managed by the container, legacy mostly).
How can I achieve this with guice?
Unfortunately, I don't think that you are going to find anything that gives you a truly clean and satisfying implementation. Especially, I don't think that you will find anything that gives you exactly what you want without implementing at least portions of it yourself.
If I had those needs, I would make sure that my injector is created in a central InjectorFactory. If you require a large number of parameters from outside to create your injector, I would simply create it once at the very beginning of my application and then cache the injector into a static final field. This would make it available to a static method. I would bind my "fall-back" property loading to an explicit provider. That way, instead of using the standard Names.bindProperties(...) method, I would bind it directly to a Provider. This provider then implements the logic that is necessary to perform the fallback or to merge multiple property files. Having the injector cached to a static field means that I can call a static method to access properties from a global-context outside of my injected classes.
Using your own provider seems initially unpleasant, but can provide some additional benefits. For starters, you can implement your fallback strategy exactly how you want. Additionally, you can add additional behaviors such as auto-reloading your property files, etc (not shown in my code sample).
public class InjectorFactory {
private static Injector injector = null;
public static synchronized Injector getOrCreateInjector() {
if(injector == null) {
injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
Properties properties1 = createProperties("file1.properties");
Properties properties2 = createProperties("file2.properties");
Set<Object> propertyNames = new HashSet<Object>();
propertyNames.addAll(properties1.keySet());
propertyNames.addAll(properties2.keySet());
for (Object object : propertyNames) {
String propertyName = (String) object;
bind(String.class).annotatedWith(Names.named(propertyName)).toProvider(new StringProvider(properties1, properties2, propertyName));
}
}
private Properties createProperties(String propertyFileName) {
try {
InputStream stream = InjectorFactory.class.getResourceAsStream(propertyFileName);
try {
Properties properties = new Properties();
properties.load(stream);
return properties;
} finally {
stream.close();
}
} catch (IOException exception) {
throw new RuntimeException("Could not load properties file");
}
}
});
}
return injector;
}
public static String getProperty(String propertyName) {
return getOrCreateInjector().getInstance(Key.get(String.class, Names.named(propertyName)));
}
}
Given the above code and file1.properties:
property1=Property1Value
property2=Property2Value
And file.properties:
property2=IncorrectProperty2Value
property3=Property3Value
with the provider
public class StringProvider implements Provider<String> {
private Properties properties1;
private Properties properties2;
private String propertyName;
public StringProvider(Properties properties1, Properties properties2,
String propertyName) {
this.properties1 = properties1;
this.properties2 = properties2;
this.propertyName = propertyName;
}
public String get() {
if(properties1.containsKey(propertyName)) {
return properties1.getProperty(propertyName);
}
return properties2.getProperty(propertyName);
}
}
The following usage:
public class InjectorFactoryTest {
public static void main(String ... parameters) {
System.out.println(InjectorFactory.getProperty("property1"));
System.out.println(InjectorFactory.getProperty("property2"));
System.out.println(InjectorFactory.getProperty("property3"));
}
}
Outputs:
Property1Value
Property2Value
Property3Value

Remote EJB Injection via #Produces - ClassCastException

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.

Dynamic target for declarative service in OSGI

Given a consumer which uses a service, how can this consumer select a specific provider dynamically using declarative service ?
Example
Service.java
public interface Service {
public void do();
}
Provider1.java
public class Provider1 implements Service {
#Override
public void do(){
//a way
}
}
Provider2.java
public class Provider2 implements Service {
#Override
public void do(){
//another way
}
}
Consumer.java
public class Consumer {
private Service myService;
protected void bindService(Service s){ // Actually it's Provider1
myService = s;
}
protected void unbindService(Service s){
myService = null;
}
public void useThisKindOfService(String s){
// Do something crazy
}
}
So, what I would like it's instead of "Do something crazy", to find a way to reconfigure the consumer in order to release Provider1 and ask for Provider2.
Is it possible ?
Update related to "Duplicate Question"
OSGI/Felix Declarative services: How to filter the services to be bound
In my context I cannot use the declarative target because the value of the target has to be know at build time, in my case the target could be defined by a user at runtime.
Components of Declarative Services can be configured via ConfigurationAdmin. By doing that, the configuration of the component can be changed at runtime.
You can also change the configuration of myService.target via ConfigurationAdmin at runtime. If you do that, another reference will be bound to your component.
If the policy of the reference of your component is dynamic, the new reference will be bound without reactivating your component.
For more information, see the Declarative Services chapter of the OSGi Compendium specification.

RPC call - static methods is not working

I am trying to define a static method in the service interface to make an rpc call. But it doesn't allow me to do so. here I am pasting my code
Client class
public void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics) {
String url = GWT.getModuleBaseURL() + "domesticservice";
domesticServiceAsync = (DomesticServiceAsync) GWT.create(DomesticService.class);
ServiceDefTarget endpoint = (ServiceDefTarget) domesticServiceAsync;
endpoint.setServiceEntryPoint(url);
domesticServiceAsync.sendDomesticData(product,dma,yrmnths,dist,metrics,new Domestichandler<Void>() );
}
public class Domestichandler<Void> implements AsyncCallback<Void> {
#Override
public void onFailure(Throwable caught) {
String error = caught.getMessage();
System.out.println(error);
}
public void onSuccess(Void result) {
System.out.println("perfect");
}
}
Service
public interface DomesticService extends RemoteService {
public void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics);
}
public interface DomesticServiceAsync {
void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics,AsyncCallback<Void> callback);
}
Server side -
public void sendDomesticData(String product, String dma, String yrmnths, String dist, String metrics) {
System.out.println(product);
}
Basically I am trying to send the values from the front interface to the server side and I don't want any return value. But the values passed to the server side should be stored globally in the server class so i can access those values in different method. I tried changing all the senddomestic values to static but it won't allow me to do so? why?
Because RemoteServiceServlet needs to invoke your service methods somehow and the implementation expects instance methods. But this shouldn't prevent you from assigning the method data to static fields. Just be aware of multi threading.
GWT always uses instance methods for RPC calls, static methods are not possible in this case.
What is important to understand about GWT is that any RemoteServiceServlet instances are created and maintained by the servlet container (e.g. Tomcat). The servlet container might create a number of servlet instances on startup (Tomcat creates 6 RemoteServiceServlet instances by default) and then uses load balancing to determine which servlet handles an RPC request at a particular point in time. Depending on settings of course, you have little control over which RemoteServiceServlet instance exactly will handle a specific RPC request.
Therefore, if you want to store information on the server side globally using RPC calls, the idea proposed by YuPPie to use static fields of your RemoteServiceServlet implementation is a BAD idea. You will have no idea which of the RemoteServiceServlet instances maintained by the server contains your static data, and any subsequent calls to retrieve the data will give erratic results.
You have a few options, though. Storing the information in a database (or something similar) is the most straightforward option, but from your post I'm guessing you want something simpler. A singleton class which holds your data is probably the way to go. A thread-safe example:
public class DataContainer
{
private static DataContainer _singleton;
private String _dataField1;
public static synchronized DataContainer getInstance()
{
if (_singleton == null)
_singleton = new DataContainer();
return _singleton;
}
public synchronized String getDataField1()
{
return _dataField1;
}
public synchronized void setDataField1(String dataField1)
{
_dataField1 = dataField1;
}
}
Then in the server side implementation of your RPC call you could do something like:
public void sendDomesticData(String product, String dma, String yrmnths, String dist, String metrics)
{
DataContainer.getInstance().setDataField1(product);
}
This way, if there are multiple servlet instances they will all share the singleton instance of DataContainer, thus giving you a place to store your data globally. I hope this will help you.

Categories

Resources