SpringBoot > Programmatically add Jetty server connector ports at runtime? - java

Using spring-boot and jetty, I'd like to be able to configure my application to listen on additional ports, which are programmatically added at runtime (+ removed?).
What I've tried:
I've followed this tutorial, which allows me to listen on multiple ports. This works perfectly, but unfortunately only works at startup only.
I've tried #Autowiring a org.eclipse.jetty.server.Server class into a service, so that I can add connectors - I got the error No qualifying bean of type [org.eclipse.jetty.server.Server] found ...
build.gradle (dependencies
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.6.RELEASE")
}
}
apply plugin: 'spring-boot'
...
compile("org.springframework.boot:spring-boot-starter-web") {
exclude module: "spring-boot-starter-tomcat"
}
compile "org.springframework.boot:spring-boot-starter-jetty"
compile "org.eclipse.jetty:jetty-proxy:9.2.17.v20160517"
...
Not sure what to try from here...

You can get hold of the Jetty Server from Boot's JettyEmbeddedServletContainer which is available from the EmbeddedWebApplicationContext. Once you've got hold of the Server you can then add new connectors to it using Jetty's API.
Here's an example that adds a new connector in response to the ApplicationReadyEvent being published:
#Bean
public JettyCustomizer jettyCustomizer(EmbeddedWebApplicationContext context) {
return new JettyCustomizer(
(JettyEmbeddedServletContainer) context.getEmbeddedServletContainer());
}
static class JettyCustomizer implements ApplicationListener<ApplicationReadyEvent> {
private final JettyEmbeddedServletContainer container;
JettyCustomizer(JettyEmbeddedServletContainer container) {
this.container = container;
}
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
Server server = this.container.getServer();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8081);
server.addConnector(connector);
try {
connector.start();
}
catch (Exception ex) {
throw new IllegalStateException("Failed to start connector", ex);
}
}
}
You should see in the logs the default connector starting on port 8080 and then the second connector starting on 8081:
2016-08-16 10:28:57.476 INFO 71330 --- [ main] o.e.jetty.server.AbstractConnector : Started ServerConnector#64bc21ac{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2016-08-16 10:28:57.478 INFO 71330 --- [ main] .s.b.c.e.j.JettyEmbeddedServletContainer : Jetty started on port(s) 8080 (http/1.1)
2016-08-16 10:28:57.482 INFO 71330 --- [ main] o.e.jetty.server.AbstractConnector : Started ServerConnector#664a9613{HTTP/1.1,[http/1.1]}{0.0.0.0:8081}
2016-08-16 10:28:57.483 INFO 71330 --- [ main] sample.jetty.SampleJettyApplication : Started SampleJettyApplication in 1.838 seconds (JVM running for 2.132)

Related

spring boot cold start taking too long on aws lambda and boot initializes twice

this is strange but my spring boot api taking much longer that expected when deployed on aws lambda.
in the cloudwatch log, i see spring boot is starting up twice first with default profile and second with a profile i set.
Why should it boot twice.. that is significantly costing time..
Source Code:
lambdahandler.java
public class LambdaHandler implements RequestStreamHandler {
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
handler.activateSpringProfiles("lambda");
} catch (ContainerInitializationException e) {
// Re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}
application.java
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
both these files are in the same package
config.java
#Configuration
#EnableWebMvc
#Profile("lambda")
public class Config {
/**
* Create required HandlerMapping, to avoid several default HandlerMapping instances being created
*/
#Bean
public HandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
/**
* Create required HandlerAdapter, to avoid several default HandlerAdapter instances being created
*/
#Bean
public HandlerAdapter handlerAdapter() {
return new RequestMappingHandlerAdapter();
}
..
..
}
pom.xml
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>aws-serverless-java-container-spring</artifactId>
<version>[0.1,)</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>3.1.0</version>
</dependency>
cloudwatch log
07:16:51.546 [main] INFO com.amazonaws.serverless.proxy.internal.LambdaContainerHandler - Starting Lambda Container Handler
:: Spring Boot ::
2020-09-05 07:16:52.724 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Starting LambdaRTEntry on 169.254.184.173 with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1051 in /)
2020-09-05 07:16:52.726 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : No active profile set, falling back to default profiles: default
2020-09-05 07:16:52.906 INFO 1 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#1e81f4dc: startup date [Sat Sep 05 07:16:52 UTC 2020]; root of context hierarchy
..
..
2020-09-05 07:16:57.222 INFO 1 --- [ main] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 40 ms
:: Spring Boot ::
2020-09-05 07:16:57.442 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : Starting LambdaRTEntry on 169.254.184.173 with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1051 in /)
2020-09-05 07:16:57.442 INFO 1 --- [ main] lambdainternal.LambdaRTEntry : The following profiles are active: lambda
2020-09-05 07:16:57.445 INFO 1 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#5ef60048: startup date [Sat Sep 05 07:16:57 UTC 2020]; root of context hierarchy
Why should it boot twice ?
I suspect your code change with activateSpringProfiles force reinitialisation.
handler.activateSpringProfiles("lambda");
https://github.com/awslabs/aws-serverless-java-container/blob/master/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringBootLambdaContainerHandler.java#L149
Try setting active profile with env variable SPRING_PROFILES_ACTIVE as part of lambda configuration file.
Java and serverless
If you use java for serverless application like AWS lambdas I would recommend for looking a framework which supports Ahead-of-Time compilation which will boost a lot your application start.
For instance have a look at Micronaut, Quarkus using with Graalvm.
Spring Boot is not the best option using with directly with AWS lambdas.

Flyway unable to set schema in java spring?

my problem is that I cannot perform a migration from flyway java spring, even though the migration files are detected, and the same migration files work from cmd.
I have already tried to set all possibly useful parameters I found on the internet to configure the schema, but it still sticks at "PUBLIC"
First of all the problem is as below: (logs from Java spring)
"2019-07-01 15:06:04.296 INFO 296 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema "PUBLIC": << Empty Schema >>
2019-07-01 15:06:04.297 INFO 296 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema "PUBLIC" to version 1 - Create person table
2019-07-01 15:06:04.324 INFO 296 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema "PUBLIC" to version 2 - Add people
2019-07-01 15:06:04.339 INFO 296 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema "PUBLIC" to version 3 - Add people2
2019-07-01 15:06:04.356 INFO 296 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 3 migrations to schema "PUBLIC" (execution time 00:00.094s)"
The table is called public, and I also cannot see it from mysql workbench.
But if I do it from command line with flyway migrate, it alters the schema called td, which is my intention:
"Migrating schema `td` to version 1 - Create person table
Migrating schema `td` to version 2 - Add people
Successfully applied 2 migrations to schema `td` (execution time 00:00.207s)"
The flyway config for Java:
public static void main(String[] args) {
Flyway flyway = new Flyway();
flyway.setBaselineOnMigrate(true);
flyway.migrate();
SpringApplication.run(TimeReportApplication.class, args);
}
application.properties:
flyway.user=root
flyway.password=root
flyway.url=jdbc:mysql://localhost:3306/td
flyway.schemas=TD
The working flyway config for command line:
flyway.url=jdbc:mysql://localhost:3306/td
flyway.user=root
flyway.password=root
Do you have any suggestions what could go wrong?
So after a day of trying, I found a solution:
You have to add "Datasource" to your initialization file. This will be autoconfigured by Spring from your application.properties file, which you have to place in src/main/resources:
public class TimeReportApplication {
#Autowired
static DataSource dataSource;
public static void main(String[] args) {
PrintLog.print("Server started");
System.out.println("Server started");
Flyway flyway = new Flyway();
flyway.clean();
flyway.setDataSource(dataSource);
flyway.setSqlMigrationPrefix("V");
flyway.setBaselineOnMigrate(true);
flyway.migrate();
SpringApplication.run(TimeReportApplication.class, args);
}
}
In your application.properties file write before each parameter "spring", e.g.:
spring.flyway.user=root

Spring Boot Controller not mapping

I have used STS and now I am using IntelliJ Ultimate Edition but I am still getting the same output. My controller is not getting mapped thus showing 404 error. I am completely new to Spring Framework.
DemoApplication.java
package com.webservice.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
HelloController.java
package com.webservice.demo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class HelloController {
#RequestMapping("/hello")
public String sayHello(){
return "Hey";
}
}
Console Output
com.webservice.demo.DemoApplication : Starting DemoApplication on XFT000159365001 with PID 11708 (started by Mayank Khursija in C:\Users\Mayank Khursija\IdeaProjects\demo)
2017-07-19 12:59:46.150 INFO 11708 --- [ main] com.webservice.demo.DemoApplication : No active profile set, falling back to default profiles: default
2017-07-19 12:59:46.218 INFO 11708 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#238e3f: startup date [Wed Jul 19 12:59:46 IST 2017]; root of context hierarchy
2017-07-19 12:59:47.821 INFO 11708 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8211 (http)
2017-07-19 12:59:47.832 INFO 11708 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2017-07-19 12:59:47.832 INFO 11708 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.15
2017-07-19 12:59:47.944 INFO 11708 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2017-07-19 12:59:47.944 INFO 11708 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1728 ms
2017-07-19 12:59:47.987 INFO 11708 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-07-19 12:59:48.510 INFO 11708 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-07-19 12:59:48.519 INFO 11708 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
2017-07-19 12:59:48.634 INFO 11708 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8211 (http)
2017-07-19 12:59:48.638 INFO 11708 --- [ main] com.webservice.demo.DemoApplication : Started DemoApplication in 2.869 seconds (JVM running for 3.44)
I too had the similar issue and was able to finally resolve it by correcting the source package structure following this
Your Controller classes are not scanned by the Component scanning. Your Controller classes must be nested below in package hierarchy to the main SpringApplication class having the main() method, then only it will be scanned and you should also see the RequestMappings listed in the console output while Spring Boot is getting started.
Tested on Spring Boot 1.5.8.RELEASE
But in case you prefer to use your own packaging structure, you can always use the #ComponentScan annotation to define your basePackages to scan.
Because of DemoApplication.class and HelloController.class in the same package
Locate your main application class in a root package above other classes
Take look at Spring Boot documentation Locating the Main Application Class
Using a root package also allows component scan to apply only on your
project.
For example, in your case it looks like below:
com.webservice.demo.DemoApplication
com.webservice.demo.controller.HelloController
In my case, it was missing the dependency from pom.xml, otherwise everything compiled just fine. The 404 and missing mappings info from Spring logs were the only hints.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
I also had trouble with a similar issue and resolved it using the correct package structure as per below. After correction, it is working properly.
e.g.
Spring Application Main Class is in package com.example
Controller Classes are in package com.example.controller
Adding #ComponentScan(com.webservice) in main class above #SpringBootApplication will resolve your problem. Refer below code
package com.webservice.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#ComponentScan(com.webservice)
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
In my case, I was using #Controller instead of #RestController with #RequestMapping
In my opinion, this visibility problem comes when we leave the component scan to Spring which has a particular way of looking for the classes using standard convention.
In this scenario as the Starter class(DemoApplication)is in com.webservice.demo package, putting Controller one level below will help Spring to find the classes using the default component scan mechanism. Putting HelloController under com.webservice.demo.controller should solve the issue.
It depends on a couple of properties:
server.contextPath property in application properties. If it's set to any value then you need to append that in your request url. If there is no such property then add this line in application.properties server.contextPath=/
method property in #RequestMapping, there does not seem to be any value and hence, as per documentation, it should map to all the methods. However, if you want it to listen to any particular method then you can set it to let's say method = HttpMethod.GET
I found the answer to this. This was occurring because of security configuration which is updated in newer versions of Spring Framework. So i just changed my version from 1.5.4 to 1.3.2
In my case I used wrong port for test request - Tomcat was started with several ones exposed (including one for monitoring /actuator).
In my case I changed the package of configuration file. Moved it back to the original com.example.demo package and things started working.
Another case might be that you accidentally put a Java class in a Kotlin sources directory as I did.
Wrong:
src/main
┕ kotlin ← this is wrong for Java
┕ com
┕ example
┕ web
┕ Controller.class
Correct:
src/main
┕ java ← changed 'kotlin' to 'java'
┕ com
┕ example
┕ web
┕ Controller.class
Because when in Kotlin sources directory, Java class won't get picked up.
All other packages should be an extension of parent package then only spring boot app will scan them by default.
Other option will be to use #ComponentScan(com.webservice)
package structure
👋 I set up 🍃Spring Boot Security in Maven deps. And it automatically deny access to unlogged users also for login page if you haven't change rules for it.
So I prefered my own security system and deleted this dependency.🤓
If you want to use Spring Security. You can wrote WebSecurityConfig like this:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf()
.disable()
.authorizeRequests()
//Доступ только для не зарегистрированных пользователей
.antMatchers("/registration").not().fullyAuthenticated()
//Доступ только для пользователей с ролью Администратор
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/news").hasRole("USER")
//Доступ разрешен всем пользователей
.antMatchers("/", "/resources/**").permitAll()
//Все остальные страницы требуют аутентификации
.anyRequest().authenticated()
.and()
//Настройка для входа в систему
.formLogin()
.loginPage("/login")
//Перенарпавление на главную страницу после успешного входа
.defaultSuccessUrl("/")
.permitAll()
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/");
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder());
}
}
from [https://habr.com/ru/post/482552/] (in russian)

Use Ubuntu Environment Variables in Gradle Properties File

I have an Environment Variable on an Ubuntu server, SDB_DOMAIN, that I'm trying to pass to this gradle properties file:
https://github.com/Netflix/SimianArmy/blob/master/src/main/resources/janitor.properties#L20
What's the syntax to pull environment variables into a properties file like this? I've tried a couple different ways, one example being: simianarmy.janitor.snapshots.ownerId = System.getenv("SIMIAN_OWNER_ID") but that just returns the literal value when I start the jetty server withgradlew jettRun and watch the logs.
19:55:53.957 [main] INFO c.n.s.basic.BasicSimianArmyContext - using standard class for simianarmy.client.recorder.class
19:55:54.060 [main] INFO c.n.simianarmy.aws.SimpleDBRecorder - Creating SimpleDB domain: "System.getenv(SDB_DOMAIN)"
19:55:54.122 [main] WARN c.n.simianarmy.aws.SimpleDBRecorder - Error while trying to auto-create SimpleDB domain
com.amazonaws.services.simpledb.model.InvalidParameterValueException: Value ("System.getenv(SDB_DOMAIN)") for parameter DomainName is invalid. (Service: AmazonSimpleDB; Status Code: 400; Error Code: InvalidParameterValue; Request ID: 4aabdeb2-68a5-0f49-dacd-17c96f375793)
Here is what I did. I Wanted my Spring-Boot Application to show me $HOME variable.
My application.properties file:
variable.home = #{ systemEnvironment['HOME'] }
Class that is using it:
#Component
public class SomeName implements CommandLineRunner {
#Value("${variable.home}" )
String home;
#Override
public void run(String... args) throws Exception {
System.out.println(home);
}
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
}
Spring boot starting log:
2015-12-10 17:46:07.622 INFO 5710 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2015-12-10 17:46:07.652 INFO 5710 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
/home/dogbert
2015-12-10 17:46:07.655 INFO 5710 --- [ main] com.example.DemoApplication : Started DemoApplication in 1.431 seconds (JVM running for 1.614)
and echo $HOME:
dogbert#borsuk:~$ echo $HOME
/home/dogbert
dogbert#borsuk:~$
I hope this helps.

Testing ejb with stub and openejb framework

I am trying to test an EJB that have another one injected in it.
For the tests purpose I want to use a stub for the injected EJB. I had use openEJB as framework for the EJB for the testing.
Here is the EJB :
#Stateless
#Local(IService.class)
public class Service implements IService {
#EJB
private IBean bean;
#Override
public String doService(String data) {
return bean.process(data);
}
}
The real injected EJB :
#Stateless
#Local(IBean.class)
public class Bean implements IBean {
private static Logger logger = Logger.getLogger(Bean.class);
#Override
public String process(String data) {
logger.info("Bean processing : " + data);
return "Bean processing : " + data;
}
}
The stub version of the EJB :
#Stateless
#Local(IBean.class)
public class BeanStub implements IBean {
private static Logger logger = Logger.getLogger(BeanStub.class);
#Override
public String process(String data) {
logger.info("Stub processing : " + data);
return "Stub processing : " + data;
}
}
And the JUnit test used :
public class ServiceTest {
private static Logger logger = Logger.getLogger(ServiceTest.class);
private static InitialContext context;
#BeforeClass
public static void setUpBeforeClass() throws Exception {
// openEJB
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,"org.apache.openejb.client.LocalInitialContextFactory");
p.put("openejb.altdd.prefix", "stub"); // use specific ejb-jar
p.put("openejb.descriptors.output", "true");
context = new InitialContext(p);
}
#Test
public void testServiceStub() {
try {
IService service = (IService) context.lookup("ServiceStubLocal");
assertNotNull(service);
String msg = service.doService("service");
assertEquals("Stub processing : service", msg);
} catch (NamingException e) {
logger.error(e);
fail(e.getMessage());
}
}
}
I had try to override the use of the real EJB by the stub one, using a specific ejb-jar (I want to use "BeanStub" instead of default "Bean" in my service) :
<ejb-jar>
<enterprise-beans>
<session id="ServiceStub">
<ejb-name>ServiceStub</ejb-name>
<ejb-class>tests.Service</ejb-class>
<ejb-local-ref>
<ejb-ref-name>tests.Service/bean</ejb-ref-name>
<ejb-link>BeanStub</ejb-link>
</ejb-local-ref>
</session>
</enterprise-beans>
</ejb-jar>
Unfortunatly I have a problem the EJB are declared :
Apache OpenEJB 3.1.4 build: 20101112-03:32
http://openejb.apache.org/
17:14:29,225 INFO startup:70 - openejb.home = D:\Workspace_Java\tests\testejb
17:14:29,225 INFO startup:70 - openejb.base = D:\Workspace_Java\tests\testejb
17:14:29,350 INFO config:70 - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service)
17:14:29,350 INFO config:70 - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager)
17:14:29,381 INFO config:70 - Found EjbModule in classpath: D:\Workspace_Java\tests\testejb\target\test-classes
17:14:29,412 INFO config:70 - Found EjbModule in classpath: D:\Workspace_Java\tests\testejb\target\classes
17:14:29,428 INFO config:70 - Beginning load: D:\Workspace_Java\tests\testejb\target\test-classes
17:14:29,428 INFO config:70 - AltDD ejb-jar.xml -> file:/D:/Workspace_Java/tests/testejb/target/test-classes/META-INF/stub.ejb-jar.xml
17:14:29,850 INFO config:70 - Beginning load: D:\Workspace_Java\tests\testejb\target\classes
17:14:29,850 INFO config:70 - AltDD ejb-jar.xml -> file:/D:/Workspace_Java/tests/testejb/target/classes/META-INF/stub.ejb-jar.xml
17:14:29,850 INFO config:70 - Configuring enterprise application: classpath.ear
17:14:29,912 INFO config:70 - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container)
17:14:29,912 INFO config:70 - Auto-creating a container for bean ServiceStub: Container(type=STATELESS, id=Default Stateless Container)
17:14:29,912 INFO options:70 - Using 'openejb.descriptors.output=true'
17:14:29,912 INFO options:70 - Using 'openejb.descriptors.output=true'
17:14:29,928 INFO config:70 - Dumping Generated ejb-jar.xml to: C:\TEMP\ejb-jar-6391test-classes.xml
17:14:29,959 INFO config:70 - Dumping Generated openejb-jar.xml to: C:\TEMP\openejb-jar-6392test-classes.xml
17:14:29,959 INFO options:70 - Using 'openejb.descriptors.output=true'
17:14:29,959 INFO config:70 - Dumping Generated ejb-jar.xml to: C:\TEMP\ejb-jar-6393classes.xml
17:14:29,975 INFO config:70 - Dumping Generated openejb-jar.xml to: C:\TEMP\openejb-jar-6394classes.xml
17:14:30,006 INFO config:70 - Enterprise application "classpath.ear" loaded.
17:14:30,084 INFO startup:70 - Assembling app: classpath.ear
17:14:30,131 INFO startup:70 - Jndi(name=ServiceStubLocal) --> Ejb(deployment-id=ServiceStub)
17:14:30,131 ERROR startup:46 - Jndi name could not be bound; it may be taken by another ejb. Jndi(name=openejb/Deployment/ServiceStub/tests.IService!Local)
17:14:30,131 INFO startup:70 - Undeploying app: classpath.ear
17:14:30,147 ERROR startup:50 - Application could not be deployed: classpath.ear
org.apache.openejb.OpenEJBException: Creating application failed: classpath.ear: Unable to bind business local interface for deployment ServiceStub
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:679)
at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:450)
Is there something wrong in the approach, or in the way to write the ejb-jar ?
I had similar problems and hurdles with OpenEJB. If you need stubbing and mocking for tests (who doesn't) have a look who I finally managed to handle it (with a great help from David - OpenEJB co-founder). In the latest version (3.1.4) OpenEJB works pretty much like Arquillian, allowing inner-class test driver, without ejb-jar.xml and classpath scanning.
I've described my hurdles here: http://jakub.marchwicki.pl/posts/2011/07/01/testing-ejb-application-openejb-without-classpath-scanning/. Have a look, maybe that will make your testing easier.
Why don't you simply use a mocking framework like EasyMock or Mockito to test this. You wouldn't need any deployment descriptor, EJB container, JNDI lookup, etc. Just this kind of code :
#Test
public void testDoService() {
IBean mockBean = EasyMock.createMock(IBean.class);
mockBean.process("data");
EasyMock.replay(mockBean);
Service serviceToTest = new Service(mockBean);
serviceTotest.doService("data");
EasyMock.verify(mockBean);
}
And it would certainly run much faster, too.

Categories