We work with a Websphere cluster with two apps in it (app1 and app2). I can call EJBs from both apps from my local spring boot out of Intellij-Idea.
When I deploy the spring boot application to the companies private openshift cloud then there is a marshalling exception in one of the two services (client side) talking to one of the websphere apps.
#Service
public class App1BookingsManagementService implements EjbService<DomainFacade> {
private static final String DOMAINFACADE_EJB_NAME = "ejb/org/company/app/DomainFacade";
private static final Logger LOG = LoggerFactory.getLogger(App1BookingsManagementService.class);
#Autowired
private InitialContext initialContext;
public List<BookingDataRow> getBookingsToday() {
DomainFacade domainFacade = null;
try {
domainFacade = createEjbStub();
BookingSelektionskriterien selektionskriterien = new BookingSelektionskriterien();
selektionskriterien.setTerminFrom(valueOf(now()));
selektionskriterien.setTerminUntil(valueOf(now().plusDays(1)));
ResultList<BookingDataRow> resultList = domainFacade.sucheBookingsDataRows(selektionskriterien);
if (resultList == null) {
LOG.warn("Error in EJB {} from APP1 returned null. ", DOMAINFACADE_EJB_NAME);
}
LOG.info("Calling EJB sucheBookingsDataRows returned {} results. ", resultList.getList().size());
return resultList.getList();
} catch (NamingException | CreateException | RemoteException | DomainException e) {
LOG.error("Error in calling EJB {} from APP1: {}, {}", DOMAINFACADE_EJB_NAME, e.getClass().getName(), e.getMessage());
} finally {
if (domainFacade != null) {
try {
domainFacade.remove();
} catch (RemoteException | RemoveException e) {
LOG.error("could not remove ejb {}", DOMAINFACADE_EJB_NAME, e);
}
}
}
return Collections.emptyList();
}
public DomainFacade createEjbStub() throws NamingException, RemoteException, CreateException {
Object stub = initialContext.lookup(DOMAINFACADE_EJB_NAME);
DomainFacadeHome facadeHome = (DomainFacadeHome) PortableRemoteObject.narrow(stub, DomainFacadeHome.class);
return facadeHome.create();
}
}
Here the config:
#Configuration
public class CorbaConfig {
#Value("${corba-location}")
private String corbaLocation;
#Value("classpath:sas.client.props")
private Resource sasClientProps;
#Bean
public InitialContext corbaContext() throws NamingException, IOException {
Hashtable<String, Object> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
env.put(PROPS.HOSTNAME_NORMALIZER, PROPS.HOSTNAME_NORMALIZER_NONE);
env.put("com.ibm.CORBA.ConfigURL", sasClientProps.getInputStream());
env.put(Context.PROVIDER_URL, corbaLocation);
return new InitialContext(env);
}
#Bean
public MBeanServer mbeanServer() {
MBeanServerFactoryBean factory = new MBeanServerFactoryBean();
factory.setLocateExistingServerIfPossible(true);
factory.afterPropertiesSet();
return factory.getObject();
}
}
This is the error I see in openshift:
2021-02-04 08:14:53,714 [ERROR] P=891120:O=0:CT appname=app-ejbcall corid= c.s.n.e.s.App1BookingsManagementService: Error in calling EJB ejb/org/company/app/DomainFacade from APP1: java.rmi.MarshalException, CORBA MARSHAL 0x4942f89a No; nested exception is:
org.omg.CORBA.MARSHAL:
>> SERVER (id=431c9d95, host=was.company.org) TRACE START:
>> org.omg.CORBA.MARSHAL: Unable to read value from underlying bridge : Default data must be read first vmcid: IBM minor code: 89A completed: No
>> at com.ibm.rmi.iiop.CDRReader.read_value(CDRReader.java:1644)
>> at com.ibm.rmi.iiop.EncoderInputStream.read_value(EncoderInputStream.java:970)
>> at org.company.app.Domain.ejb.facade._EJSRemoteStatelessDomainFacade_a1729579_Tie.sucheBookingenDataRows(Unknown Source)
>> at org.company.app.Domain.ejb.facade._EJSRemoteStatelessDomainFacade_a1729579_Tie._invoke(Unknown Source)
>> at com.ibm.CORBA.iiop.ServerDelegate.dispatchInvokeHandler(ServerDelegate.java:638)
>> at com.ibm.CORBA.iiop.ServerDelegate.dispatch(ServerDelegate.java:508)
>> at com.ibm.rmi.iiop.ORB.process(ORB.java:613)
>> at com.ibm.CORBA.iiop.ORB.process(ORB.java:1584)
>> at com.ibm.rmi.iiop.Connection.doRequestWork(Connection.java:3190)
>> at com.ibm.rmi.iiop.Connection.doWork(Connection.java:3051)
>> at com.ibm.rmi.iiop.WorkUnitImpl.doWork(WorkUnitImpl.java:64)
>> at com.ibm.ws.giop.threadpool.WorkQueueElement.dispatch(WorkQueueElement.java:174)
>> at com.ibm.ws.giop.filter.GiopFilterChain.processMessage(GiopFilterChain.java:203)
>> at com.ibm.ws.giop.threadpool.PooledThread.handleRequest(PooledThread.java:81)
>> at com.ibm.ws.giop.threadpool.PooledThread.run(PooledThread.java:102)
>> at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1892)
>> SERVER (id=431c9d95, host=was.company.org) TRACE END.
vmcid: IBM minor code: 89A completed: No
Locally there is no problem talking to the same websphere server. One guess was the RMI stub is not the same but then it would be also a problem locally, no?
App 1
App 2
Local Spring Boot run in IDE
OK
OK
Deployt in Openshift Cloud
OK
NOK
The problem was that I configured the build with jdk-8 but at runtime the jre was set to version 11.
So I had to find the internal configuration for the correct docker file to use java 8.
Related
I am trying to run acceptance tests with concordion fixtures in a quarkus project. Concordion does not work with Junit5 so I am using its original #Run(ConcordionRunner.class).
I am creating a superclass to start my quarkus application before tests like that:
#RunWith(ConcordionRunner.class)
public abstract class AbstractFixture {
public static RunningQuarkusApplication application;
protected static RequestSpecification server;
protected AbstractFixture() {
setUp();
}
public void setUp() {
if(application == null) {
startApplication();
server = new RequestSpecBuilder()
.setPort(8081)
.setContentType(ContentType.JSON)
.build();
}
}
private void startApplication() {
try {
PathsCollection.Builder rootBuilder = PathsCollection.builder();
Path testClassLocation = PathTestHelper.getTestClassesLocation(getClass());
rootBuilder.add(testClassLocation);
final Path appClassLocation = PathTestHelper.getAppClassLocationForTestLocation(
testClassLocation.toString());
rootBuilder.add(appClassLocation);
application = QuarkusBootstrap.builder()
.setIsolateDeployment(false)
.setMode(QuarkusBootstrap.Mode.TEST)
.setProjectRoot(Paths.get("").normalize().toAbsolutePath())
.setApplicationRoot(rootBuilder.build())
.build()
.bootstrap()
.createAugmentor()
.createInitialRuntimeApplication()
.run();
} catch (BindException e) {
e.printStackTrace();
System.out.println("Address already in use - which is fine!");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
The code above is working but I can't change the default port 8081 to any other.
If I print the config property in my Test class like below, it prints the port correctly, but quarkus is not running on it:
public class HelloFixture extends AbstractFixture {
public String getGreeting() {
Response response = given(server).when().get("/hello");
System.out.println("Config[port]: " + application.getConfigValue("quarkus.http.port", String.class));
return response.asString();
}
}
How can I specify the configuration file or property programatically before run?
I found the answer. At first, I was referencing the wrong property "quarkus.http.port" instead of "quarkus.http.test-port".
Despite that, I found the way to override properties before run:
...
StartupAction action = QuarkusBootstrap.builder()
.setIsolateDeployment(false)
.setMode(QuarkusBootstrap.Mode.TEST)
.setProjectRoot(Paths.get("").normalize().toAbsolutePath())
.setApplicationRoot(rootBuilder.build())
.build()
.bootstrap()
.createAugmentor()
.createInitialRuntimeApplication();
action.overrideConfig(getConfigOverride());
application = action.run();
...
private Map<String, String> getConfigOverride() {
Map<String, String> config = new HashMap<>();
config.put("quarkus.http.test-port", "18082");
return config;
}
I'm trying to implement XA transactions in my Spring Boot app across Hazelcast and JPA persisting to PostgreSQL. Putting the Atomikos Spring Boot starter in my pom.xml got it to load the JtaTransactionManager to be used with the #Transactional annotations, but the Hazelcast XA Resource is not being enlisted with the transaction.
How do I get Spring Boot to automatically enlist my XA Resources with the JTA UserTransaction as part of the AOP transaction interceptor that's using the JtaTransactionManager?
I solved this by using an annotation and AspectJ Aspect as described here. Also see this for defining the pointcuts to match either class or method level annotations, and you may need to do this:
#EnableTransactionManagement(order = Ordered.HIGHEST_PRECEDENCE)
to have the transaction interceptor happen before this code is called.
#Aspect
#Component
public class XAResourceAspect {
#Autowired
JtaTransactionManager jtaTransactionManager;
#Autowired
ApplicationContext applicationContext;
#Pointcut("within(#XAResource *)")
public void beanAnnotatedWithAnnotation() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("publicMethod() && beanAnnotatedWithAnnotation()")
public void publicMethodInsideAnnotatedClass() {}
private ThreadLocal<Map<Transaction, Set<String>>> enlistedResources = new ThreadLocal<>();
#Around("#annotation(ppi.nestup.v3.annotation.XAResource) || publicMethodInsideAnnotatedClass()")
public Object enlistResources(ProceedingJoinPoint joinPoint) throws Throwable {
boolean setThreadLocal = false;
Transaction transaction = jtaTransactionManager.getTransactionManager().getTransaction();
if (transaction != null) {
Map<Transaction, Set<String>> transactionMap = enlistedResources.get();
LOG.info("Enlisting resources for joinpoint " + joinPoint + " and transaction " + transaction);
if (transactionMap == null) {
transactionMap = new HashMap<>();
enlistedResources.set(transactionMap);
setThreadLocal = true;
LOG.info("Created new ThreadLocal for transaction " + transaction);
} else {
LOG.info("Found existing ThreadLocal " + transactionMap);
}
transactionMap.computeIfAbsent(transaction, k -> new HashSet<>());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Class withinType = joinPoint.getSourceLocation().getWithinType();
XAResource annotation = method.getAnnotation(XAResource.class);
if (annotation == null) {
annotation = (XAResource) withinType.getAnnotation(XAResource.class);
}
String[] resourceNames = annotation.value();
for (String name : resourceNames) {
if (!transactionMap.get(transaction).contains(name)) {
javax.transaction.xa.XAResource resource =
(javax.transaction.xa.XAResource) applicationContext.getBean(name);
try {
transaction.enlistResource(resource);
} catch (IllegalStateException e) {
LOG.error("Caught exception trying to enlist resource " + name + " for transaction " + transaction + " and joinpoint " + joinPoint);
e.printStackTrace();
}
transactionMap.get(transaction).add(name);
}
}
}
Object proceed = joinPoint.proceed();
if (setThreadLocal) {
LOG.info("Removing threadlocal");
enlistedResources.remove();
}
return proceed;
}
}
I haven't done a lot of testing of this yet, but it's working so far.
I have created spring boot thread pool project which has thread that needs to run 24x7 once spawned but when I need to stop the app in server for some maintenance it should shutdown after completing its current task and not taking up any new task.
My code for the same is:
Config class
#Configuration
public class ThreadConfig {
#Bean
public ThreadPoolTaskExecutor taskExecutor(){
ThreadPoolTaskExecutor executorPool = new ThreadPoolTaskExecutor();
executorPool.setCorePoolSize(10);
executorPool.setMaxPoolSize(20);
executorPool.setQueueCapacity(10);
executorPool.setWaitForTasksToCompleteOnShutdown(true);
executorPool.setAwaitTerminationSeconds(60);
executorPool.initialize();
return executorPool;
}
}
Runnable class
#Component
#Scope("prototype")
public class DataMigration implements Runnable {
String name;
private boolean run=true;
public DataMigration(String name) {
this.name = name;
}
#Override
public void run() {
while(run){
System.out.println(Thread.currentThread().getName()+" Start Thread = "+name);
processCommand();
System.out.println(Thread.currentThread().getName()+" End Thread = "+name);
if(Thread.currentThread().isInterrupted()){
System.out.println("Thread Is Interrupted");
break;
}
}
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void shutdown(){
this.run = false;
}
}
Main class:
#SpringBootApplication
public class DataMigrationPocApplication implements CommandLineRunner{
#Autowired
private ThreadPoolTaskExecutor taskExecutor;
public static void main(String[] args) {
SpringApplication.run(DataMigrationPocApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
for(int i = 1; i<=20 ; i++){
taskExecutor.execute(new DataMigration("Task " + i));
}
for (;;) {
int count = taskExecutor.getActiveCount();
System.out.println("Active Threads : " + count);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 0) {
taskExecutor.shutdown();
break;
}
}
System.out.println("Finished all threads");
}
}
I need help to understand if I need to stop my spring boot application it should stop all the 20 threads running which runs (24x7) otherwise after completing there current loop in while loop and exit.
I would propose couple of changes in this code to resolve the problem
1) since in your POC processCommand calls Thread.sleep, when you shutdown the executor and it interrupts workers InterruptedException get called but is almost ignored in your code. After that there is if(Thread.currentThread().isInterrupted()) check which will return false for the reason above. Similar problem is outlined in the post below
how does thread.interrupt() sets the flag?
the following code change should fix the problem:
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
shutdown();
}
}
2) Also because of ThreadConfig::taskExecutor executorPool.setWaitForTasksToCompleteOnShutdown(true) Spring will call executor.shutdown instead of executor.shutdownNow. According to javadoc ExecutorService.shutdown
Initiates an orderly shutdown in which previously submitted tasks are
executed, but no new tasks will be accepted.
So I would recommend to set
executorPool.setWaitForTasksToCompleteOnShutdown(false);
Other things to improve in this code: although DataMigration is annotated as a component the instances of this class are creared not by Spring. You should try using factory method similar to ThreadConfig::taskExecutor in order to make Spring initiate instances of DataMigration for example to inject other bean into DataMigration instances.
In order to shutdown executor when running jar file on linux environment you can for example add actuator module and enable shutdown endpoint:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
in application.properties:
endpoints.shutdown.enabled=true
It will enable JMX shutdown endpoint and you can call shutdown on it.
If you want current job cycle of the task to be finished you should set
executorPool.setWaitForTasksToCompleteOnShutdown(true);
In order to connect to your jvm process on linux env remotely you have to specify an RMI Registry port.
Here is a detailed article:
How to access Spring-boot JMX remotely
If you just need to connect to JMX from local env you can run jsoncole or command-line tools : Calling JMX MBean method from a shell script
Here is an example uf using one of these tools - jmxterm
$>run -d org.springframework.boot: -b org.springframework.boot:name=shutdownEndpoint,type=Endpoint shutdown
#calling operation shutdown of mbean org.springframework.boot:name=shutdownEndpoint,type=Endpoint with params []
#operation returns:
{
message = Shutting down, bye...;
}
Following are the modules in my project,
1. EJB module (version 3): We prepare ejb jar of this module and deploy on Weblogic11g server. It deals with database operation. It has #local, #Remote interface and #stateless classes implementing #local,#Remote interfaces.
2. Web Application : This web application takes inputs (user uploads file) from users, validates file and inserts data into database. It uses RMI.
Problem: On production (weblogic 11g server ) sometimes we observe exception saying $Proxy99 cannot be cast to "Remote interface name" (for different different classes) e.g com.xyz.fileProcessSetting.FileProcessSttgFacadeRemote.
But after some time when we again upload file, it gets uploaded successfully without any error.
Now, I do not understand how come these remote objects becomes temporarily unavailable? Never faced this issue on development/UAT environment. Also no idea how to reproduce and fix it.
Please help. Thanks in advance.
#Remote
public interface FileProcessSttgFacadeRemote {
//methods
}
#Local
public interface FileProcessSttgFacadeLocal {
//methods
}
#Stateless
public class FileProcessSttgFacade implements FileProcessSttgFacadeLocal, FileProcessSttgFacadeRemote {
//methods
}
in weblogic-ejb-jar.xml
<weblogic-enterprise-bean>
<ejb-name>FileProcessSttgFacade</ejb-name>
<stateless-session-descriptor>
<business-interface-jndi-name-map>
<business-remote>com.xyz.fileProcessSetting.FileProcessSttgFacadeRemote</business-remote>
<jndi-name>FileProcessSttgFacade</jndi-name>
</business-interface-jndi-name-map>
</stateless-session-descriptor>
</weblogic-enterprise-bean>
In web application also in ejb module whenever we want to call methods we use following lookup method to get remote object:
public class someclass extends EjbLocator {
public void someMethod(){
FileProcessSttgFacadeRemote fpfr = (FileProcessSttgFacadeRemote) getService("FileProcessSttgFacade");
//other code
}
}
Following is the class used for JNDI lookup:
public class EjbLocator {
public Object getService(final String jndiName) throws Exception {
try {
obj = getDefaultContext().lookup(jndiName);
} catch (final Exception exp) {
exp.printStackTrace();
}
return obj;
}
protected Context getDefaultContext() {
try {
final Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL,"weblogic");
env.put(Context.SECURITY_CREDENTIALS, "password");
env.put(Context.PROVIDER_URL, "t3://<ip>:<port>");
defaultContext = new InitialContext(env);
return defaultContext;
} catch (final NamingException nExp) {
nExp.printStackTrace();
}
return null;
}
}
I am trying to stop a MDB in JBoss 5.1.0.
My code is as follows :
public static void stopAllMDB()
throws InterruptedException, MalformedObjectNameException, NullPointerException {
String currentVersion = "1.0";
for (String name : Util.MDB_NAMES) {
String mbean = "jboss.j2ee:ear=myEar" + currentVersion + ".ear,jar=myJar.jar,name=myMDB,service=EJB3";
ObjectName objName = new ObjectName(mbean);
System.out.println("Stop MDB " + name);
try {
MBeanServer mbeanServer = MBeanServerLocator.locateJBoss();
MessagingDelegateWrapperMBean invoker = (MessagingDelegateWrapperMBean)MBeanProxy.get(MessagingDelegateWrapperMBean.class,
objName, mbeanServer);
invoker.stopDelivery();
}
} catch (Exception e) {
}
}
But if fails at
MessagingDelegateWrapperMBean invoker = (MessagingDelegateWrapperMBean)MBeanProxy.get(MessagingDelegateWrapperMBean.class,objName, mbeanServer)
with
Illegal argument exception.
Apparently the code I was using wasn't working. Don't know why. On a JBoss 5.1.0 GA platform the following apply :
MBeanServer mbeanServer = MBeanServerLocator.locateJBoss();
String mbean = "jboss.j2ee:ear=myEar.ear,jar=all-ejbs.jar,name=" + name + ",service=EJB3";
ObjectName objectName = new ObjectName(mbean);
mbeanServer.invoke(objectName, "stopDelivery", new Object[] {}, null);
And as a remark I didn't experience the session exception you got.