Reference EJBs outside the Application server - java

I would like to know if any of you have ever call an EJB remotely. This is my scenario:
I have a single remote interface package in its own jar file. Then there is a EJB module (another jar file) that depends on the previous one to implement the interface as a #Stateless session bean.
I have deployed the EJB module in JBOSS 5.1.0.GA. When I try calling the EJB from within Eclipse, the returned object is not recognized as being of the interface type. Below are the differents java codes.
The Business interface:
#Remote
public interface RemoteBusinessInterface
{
public CustomerResponse getCustomerData( final CustomerRequest customerRequest );
}
Implementing class package in its own jar file:
#Stateless
public class RemoteEJBBean implements RemoteBusinessInterface
{
public CustomerResponse getCustomerData( final CustomerRequest customerRequest )
{...
And The code calling the remote EJB:
public class TestRemoteEjb
{
public static void main( final String[] args )
{
try
{
InitialContext initialContext = new InitialContext();
Object ref = initialContext.lookup( "java:/CustomerServiceBean/remote" );
System.out.println( ref );
if ( ref instanceof RemoteBusinessInterface )
{
System.out.println( "RemoteBusinessInterface" );
}
else
{
System.out.println( "Not of type RemoteBusinessInterface" );
}
}
catch ( NamingException e )
{
e.printStackTrace();
}
}
}
The output reads:
Reference Class Name: Proxy for: com.tchouaffe.remote.interfaces.RemoteBusinessInterface
Type: ProxyFactoryKey
Content: ProxyFactory/remote-ejb-1.0.0-SNAPSHOT/CustomerServiceBean/CustomerServiceBean/remote
Type: EJB Container Name
Content: jboss.j2ee:jar=remote-ejb-1.0.0-SNAPSHOT.jar,name=CustomerServiceBean,service=EJB3
Type: Proxy Factory is Local
Content: false
Type: Remote Business Interface
Content: com.tchouaffe.remote.interfaces.RemoteBusinessInterface
Type: Remoting Host URL
Content: socket://127.0.0.1:3873/
Not of type RemoteBusinessInterface
I have been wondering why the returned object is of a type other than RemoteBusinessInterface.
Thanks for any help.
Edmond

Try to check the following point:
It seems to be that you are not initializing the InitialContext object.
According to JBoss 5 documentation the properties needed are:
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY,"org.jboss.naming.NamingContextFactory");
env.put(Context.URL_PKG_PREFIXES,"org.jboss.naming:org.jnp.interfaces");
env.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");
InitialContext context = new InitialContext(env);
Additionaly, try to check if your client code has the necessary dependencies. The documentation is not clear enough about what are the jar files, but this link can help you to identify them. You also need to include the jar with the remote interface.

Related

Can't find url in webLogic deployment

Tis is my first time deploying with weblogic, I have a spring boot application and when i add in weblogic than I can't find the path for my rest call, in my main application I even added this log to see where is running but i get no output:
private static final Logger LOGGER = LoggerFactory.getLogger(KpiApplication.class);
public static void main(String[] args) throws UnknownHostException {
SpringApplication app = new SpringApplication(KpiApplication.class);
Environment env = app.run(args).getEnvironment();
String protocol = "http";
if (env.getProperty("server.ssl.key-store") != null) {
protocol = "https";
}
System.out.println("LEXA"+ env.getProperty("server.port")+InetAddress.getLocalHost().getHostAddress());
try {
LOGGER.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\t{}://localhost:{}\n\t" +
"External: \t{}://{}:{}\n\t" +
"Profile(s): \t{}\n----------------------------------------------------------",
env.getProperty("spring.application.name"),
protocol,
env.getProperty("server.port"),
protocol,
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port"),
env.getActiveProfiles());
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SpringApplication.run(KpiApplication.class);
}
}
In my weblogic i see this path in this section but i just says page not found:
The rest I'm trying to execute is this but everytime is a page not found:
#RestController
public class AccountController extends KpiAbstractController {
#Autowired
private AccountService accountService;
#GetMapping("/v1/accounts")
public ResponseEntity<AccountDTO> getAccounts(#RequestParam #ApiParam("Point of sale owner ID") String ownerPosId,
#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startPeriod,
#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endPeriod,
HttpServletRequest request)
All the application is already setted up and my manager just told me try it, but he doesn't know the link too, any idea where I can find it please
Have you done what's written in Spring Boot docs "traditional deployment" part? https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.traditional-deployment
Looks like, you didn't.
You get no output from your logs, because public static void main will not be called on WebLogic - you will need to extend SpringBootServletInitializer instead.
Also for weblogic you would usually create src/main/webapp/weblogic.xml file where you would set context-root, e.g.
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.5/weblogic-web-app.xsd">
<context-root>myapp</context-root>
</weblogic-web-app>
Which make you application accessible at http://server:port/myapp URL.
Another thing you didn't mention is if you have a sole AdminServer or AdminServer+ManagedServer? If the letter, then you should deploy your application via AdminServer to ManagedServer and access it at ManagedServer host:port.

spring.main.allow-bean-definition-overriding=true is ignored and ConflictingBeanException is thrown

I am working on a spring-boot application with version 2.2.0.
I wanted to override a bean, which spring-boot provides by default. So I tried spring.main.allow-bean-definition-overriding=true in my workflow.properties file.
The code is same in both java files, one whose bean is provided by default and the one which I have created. The only difference is, the java file that I have created will load the resource which I provide it, instead of loading the default resource by default bean(from spring-boot).
I am loading workflow.properties like this in my main Application which extends org.springframework.boot.web.servlet.support.SpringBootServletInitializer:
#Override
protected SpringApplicationBuilder configure( SpringApplicationBuilder application )
{
application.application().setDefaultProperties( getProperties() );
return application.sources( WorkflowServiceApplication.class );
}
public static void main( String[] args )
{
// SpringApplication.run( WorkflowServiceApplication.class, args );
new SpringApplicationBuilder( WorkflowServiceApplication.class ).properties( "spring.config.name:workflow" )
.build()
.run( args );
}
static Properties getProperties()
{
Properties props = new Properties();
props.put( "spring.config.name", "workflow" );
props.put( "spring.config.location", "../conf/" );
return props;
}
But I'm still getting the same ConflictingBeanException
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'stencilSetResource' for bean class [org.flowable.ui.modeler.rest.app.StencilSetResource] conflicts with existing, non-compatible bean definition of same name and class [com.myApplication.workflow.workflowservice.controllers.StencilSetResource]
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:349) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:287) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:132) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:290) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:245) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:587) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
... 15 common frames omitted
After checking in spring-boot documentation, I think that by default bean definition overriding is false, but why does the overriding not work even if I set it to true in my properties file?
Edit:
After going through this documentation, it looks like the configuration that tells Environment to read properties from workflow.properties instead of application.properties happens after ApplicationContext is refreshed. Also a couple of properties like spring.main.* are read and loaded to Environment before ApplicationContext refresh happens. The link suggests to load property source into Environment by implementing EnvironmentPostProcessor, so I'll try that and see how it goes.
Edit 2:
Okay so I tried the approach given in documentation link in above edit:
#Order( Ordered.LOWEST_PRECEDENCE )
public class CustomEvironmentPostProcesor implements EnvironmentPostProcessor
{
private static final String DEFAULT_MY_FRAMEWORK_PROPERTIES = "workflowProperties";
private static final String DEFAULT_MY_FRAMEWORK_PROPERTIES_LOCATION = "/workflow.properties";
private PropertiesPropertySourceLoader propertySourceLoader = new PropertiesPropertySourceLoader();
#Override
public void postProcessEnvironment( ConfigurableEnvironment environment,
SpringApplication application )
{
Resource workflowProperties = new ClassPathResource( DEFAULT_MY_FRAMEWORK_PROPERTIES_LOCATION );
List<PropertySource<?>> propertySource;
try
{
propertySource = propertySourceLoader.load( DEFAULT_MY_FRAMEWORK_PROPERTIES, workflowProperties );
if( propertySource != null )
{
environment.getPropertySources().addLast( propertySource.get( 0 ) );
}
}
catch( IOException e )
{
e.printStackTrace();
}
}
}
I can see my workflow.properties getting loaded in Environment instance passed in postProcessEnvironment method.
But still, I get the same exception.

How to register Ratpack's ConfigurableModule using application configuration

Current HikariModule contains hard-coded value in Java code, which is not a good practice, much better would be use values defined in db.properties. How to achieve this? Do I need create a custom ConfigurableModule<MyModule.Settings> and register HikariModule inside MyModule? I have not found the way how to register a module inside a module. Thanks!
public class App {
public static void main(String[] args) throws Exception {
RatpackServer.start(s -> s
.serverConfig( configBuilder -> configBuilder
.findBaseDir()
.props("db.properties")
.require("/database", Settings.class)
)
.registry(Guice.registry( bindings -> bindings
.module(HikariModule.class, hm -> {
hm.setDataSourceClassName("org.postgresql.ds.PGSimpleDataSource");
hm.addDataSourceProperty("url", "jdbc:postgresql://localhost:5433/ratpack");
hm.setUsername("postgres");
hm.setPassword("postgres");
}).bind(DatabaseInit.class)
))
.handlers( chain -> chain
...
)
);
}
}
Let's say you have a postgres.yaml file in src/ratpack/postgres.yaml whose contents are:
db:
dataSourceClassName: org.postgresql.ds.PGSimpleDataSource
username: postgres
password: password
dataSourceProperties:
databaseName: modern
serverName: 192.168.99.100
portNumber: 5432
In that same directory let's say you have an empty .ratpack file.
From your main class you can then do this:
RatpackServer.start(serverSpec -> serverSpec
.serverConfig(config -> config
.baseDir(BaseDir.find()) // locates the .ratpack file
.yaml("postgres.yaml") // finds file relative to directory containing .ratpack file
.require("/db", HikariConfig.class) // bind props from yaml file to HikariConfig class
).registry(Guice.registry(bindings -> bindings
.module(HikariModule.class) // this will use HikariConfig to configure the module
)).handlers(...));
There's a full working example here https://github.com/danhyun/modern-java-web

How do you implement a ManagedServiceFactory in OSGi?

Im currently trying to setup my own implementation of a ManagedServiceFactory. Here is what I'm trying to do: I need multiple instances of some service on a per-configuration base. With DS the components worked perfectly but now I found out that these services should handle there own lifecycle (i.e. (de)registration at the service registry) depending on the availability of some external resource, which is impossible with DS.
Thus my idea was to create a ManagedServiceFactory, which then would receive configs from the ConfigurationAdmin and create instances of my class. These again would try to connect to the resource in a seperate thread and register themselves as service when they're ready to operate.
Since I had no luck implementing this yet, I tried to break everything down to the most basic parts, not even dealing with the dynamic (de)registration, just trying to get the ManagedServiceFacotry to work:
package my.project.factory;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;
public class Factory implements BundleActivator, ManagedServiceFactory {
private ServiceRegistration myReg;
private BundleContext ctx;
private Map<String, ServiceRegistration> services;
#Override
public void start(BundleContext context) throws Exception {
System.out.println("starting factory...");
this.ctx = context;
java.util.Dictionary properties = new Hashtable<String, Object>();
properties.put(Constants.SERVICE_PID, "my.project.servicefactory");
myReg = context.registerService(ManagedServiceFactory.class, this,
properties);
System.out.println("registered as ManagedServiceFactory");
services = new HashMap<String, ServiceRegistration>();
}
#Override
public void stop(BundleContext context) throws Exception {
for(ServiceRegistration reg : services.values()) {
System.out.println("deregister " + reg);
reg.unregister();
}
if(myReg != null) {
myReg.unregister();
} else {
System.out.println("my service registration as already null " +
"(although it shouldn't)!");
}
}
#Override
public String getName() {
System.out.println("returning facotry name");
return "ServiceFactory";
}
#Override
public void updated(String pid, Dictionary properties)
throws ConfigurationException {
System.out.println("retrieved update for pid " + pid);
ServiceRegistration reg = services.get(pid);
if (reg == null) {
services.put(pid, ctx.registerService(ServiceInterface.class,
new Service(), properties));
} else {
// i should do some update here
}
}
#Override
public void deleted(String pid) {
ServiceRegistration reg = services.get(pid);
if (reg != null) {
reg.unregister();
}
}
}
Now, it should receive configurations from the ConfigurationAdmin for PID my.project.servicefactory, shouldn't it?
But it does not receive any configurations from the ConfigurationAdmin. The bundle is started, the service is registered and in the web console, I can see the config admin holds a reference to my ManagedServiceFactory. Is there a certain property which should be set? The interface specification does not suggest that. Actually my implementation is more or less the same as the example there. I've no idea what I'm doing wrong here, any pointers to the solutions are very welcome.
Also, I orginally thought to implement the ManagedServiceFactory itself as DS, which also should be possible, but I failed at the same point: no configurations are handed over by the ConfigAdmin.
update
To clarify the question: I think that this is mainly an configuration problem. As I see it, I should be able to specify two PIDs for the factory, one which identifies a configuration for the factory itself (if any), and one which would produce services trough this factory, which I thought should be the factory.pid. But the framework constants do not hold anything like this.
update 2
After searching a bit the Felix Fileinstall source code, I found out that it treats configuration files differently when there is a - in the filename or not. Having the configuration file named my.project.servicefactory.cfg it did not work, but the configs named my.project.servicefactory-foo.cfg and my.project.servicefactory-bar.cfg were properly handed over to my ManagedServiceFactory as expected, and multiple services with ServiceInterface were registered. Hurray!
update 3
As proposed by Neil, I put the declarative service part in a new question to bound the scope of this one.
I think that the problem is you have a singleton configuration record rather than a factory record. You need to call Config Admin with the createFactoryConfiguration method using my.project.servicefactory as the factoryPid.
If you are using Apache Felix FileInstall (which is a nice easy way to create config records without writing code) then you need to create a file called my.project.servicefactory-1.cfg in the load directory. You can create further configurations with the same factoryPID by calling them my.project.servicefactory-2.cfg etc.

Can an EJB be called from a desktop application?

I am new in Java EJB 3.0. It is possible to call a (session) bean—deployed on JBoss—from a desktop application client?
Thanks in advance.
Yes you can. Some specifics are here (references EJB2 but it the same for EJB3 when it comes to remote clients): http://www.theserverside.com/discussions/thread.tss?thread_id=9197
Paraphrased:
Hashtable env = new Hashtable();
env.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
env.put("java.naming.provider.url", "jnp://localhost:1099");
env.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
Context ctx = new InitialContext(env);
// name is whatever JNDI name you gave it
Object o = ctx.lookup("home name");
EJBHome ejbHome = (EJBHome) PortableRemoteObject.narrow(o,EJBHome.class);
// This is userID should be the one passed.
EJB ejb = ejbHome.create(..);
Yes.
public static void main(String args[]) throws Exception {
InitialContext ctx = new InitialContext();
YourService yourService = (YourService) ctx.lookup("com.example.session.YourService");
String time = yourService.getTime();
System.out.println("Time is: " + time);
}
For client configuration you must provide jndi.properties file with contents
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost
If you are looking for working examples on JBoss try download source code of Enterprise JavaBeans 3.0, Fifth Edition
Let's assume you have the following remote interface:
#Remote
public interface HelloBeanRemote {
public String sayHello();
}
And a session bean implementing it:
#Stateless
public class HelloBean implements HelloBeanRemote {
...
}
And that this EJB is correctly packaged and deployed on JBoss.
On the client side, create a jndi.properties with the following content and put it on the classpath:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099
Then use the following code to call your EJB:
Context context;
try {
context = new InitialContext();
HelloBeanRemote beanRemote = (HelloBeanRemote)context.lookup("HelloBean/remote");
beanRemote.test();
} catch (NamingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
Alternatively, if you don't want to provide a jndi.properties file, you can explicitly setup the JNDI environment in the code and create the context like this:
Properties properties = new Properties();
properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
properties.put("java.naming.provider.url","localhost:1099");
Context context = new InitialContext(properties);
But I'd recommend using the jndi.properties for the sake of portability.
You can also expose the bean as a web service. I believe this is available as of EJB 3. It is quite nice considering you can do it with annotations. You may wish to consider using this option to decrease coupling. Here is a link to a tutorial.

Categories

Resources