Calling Spring Main Method from Another Class Error - spring

I am working on a Plugin for an OpenFire Server. I am trying to integrate Spring into this plugin. When the plugin is initialized, I will like to call the Main Method for my Spring.
When I execute the Spring alone, it works fine, but when I call its main method from my plugin, I get an exception.
How am I suppose to call the Spring Main Method.
What am I missing. Any help will be appreciated. Thank you.
Spring Main Class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
try {
SpringApplication.run(Application.class, args);
System.out.println("No error");
} catch (Exception e) {
System.out.println("Error " + e);
}
}
}
OpenFire Plugin:
public class FetchNewsPlugin implements Plugin {
#Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {
Runnable r = new Runnable() {
public void run() {
String[] args = {};
Application.main(args);
}
};
new Thread(r).start();
System.out.println("Plugin Intitialized");
}
#Override
public void destroyPlugin() {
}
}
Log Output:
Exception in thread "Thread-13" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
at hello.Application.main(Application.java:11)
at org.clinton.openfire.plugin.FetchNewsPlugin$1.run(FetchNewsPlugin.java:20)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 3 more
Where Exception is being thrown:
/**
* Finds and loads the class with the specified name from the URL search
* path. Any URLs referring to JAR files are loaded and opened as needed
* until the class is found.
*
* #param name the name of the class
* #return the resulting class
* #exception ClassNotFoundException if the class could not be found,
* or if the loader is closed.
* #exception NullPointerException if {#code name} is {#code null}.
*/
protected Class findClass(final String name)
throws ClassNotFoundException
{
final Class result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction>() {
public Class run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/clinton/git/Openfire/bin/build/lib/ant/slf4j-simple.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/clinton/git/Openfire/bin/build/lib/dist/slf4j-log4j12.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/clinton/git/Openfire/build/lib/ant/slf4j-simple.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/clinton/git/Openfire/build/lib/dist/slf4j-log4j12.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]

It seems like you are using URLClassLoader to load classes that your needed. However, an URLClassLoader should contain all items or jars it depends on not only the jar itself.
In other words, you'd better extract your jar and add all necessary items to your URLClassLoader.
In this way it should work. And please let me know if any problem.
And question https://stackoverflow.com/a/37339725/5619827 could be helpful.

Thanks #Gemini for helping, but I guess I was too dumb to follow his way. I found another workaround: Since the Spring could start successfully when executed alone. I bundled the Spring into a runnable jar file and executed using the following when th plugin starts:
private void startSpring() {
try {
ProcessBuilder processBuilder = new ProcessBuilder("/usr/lib/jvm/java-8-oracle/bin/java", "-jar",
"/home/clinton/git/Maven/target/mavenproject2-1.0-SNAPSHOT.jar");
processBuilder.directory(new File("/home/clinton/git/Maven/Working"));
File log = new File("log");
processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(Redirect.appendTo(log));
Process p = processBuilder.start();
assert processBuilder.redirectInput() == Redirect.PIPE;
assert processBuilder.redirectOutput().file() == log;
assert p.getInputStream().read() == -1;
System.out.println("Started success");
//Process p = processBuilder.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I hope this helps someone too. I always find a way :)

Related

How to run Quarkus programatically in Test mode

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;
}

Can't override java.util.logging.LogManager in a Spring Boot web application: Getting java.lang.ClassNotFoundException on already loaded class

I am trying to override java.util.logging.LogManager with my own configuration:
class CloudwatchHandler is an implementation of Handler and includes this init() method:
public static void init() {
final String julConfigFile = System.getProperty("java.util.logging.config.file");
if(julConfigFile != null) {
try (InputStream is = new FileInputStream(julConfigFile)) {
LogManager logManager = LogManager.getLogManager();
logManager.reset();
logManager.readConfiguration(is);
Logger logger = Logger.getLogger(CloudwatchHandler.class.getName());
logger.info("LOADED");
} catch (SecurityException | IOException e) {
System.err.println(Instant.now() + ": Failed to initialize JUL.");
e.printStackTrace(System.err);
throw new RuntimeException(e);
}
}
else {
System.err.println(Instant.now() + ": java.util.logging.config.file was not specified");
}
}
Application main class
public static void main(String[] args) {
CloudwatchHandler.init();
SpringApplication.run(MyApp.class, args);
}
Error
Can't load log handler "mypackage.CloudwatchHandler"
java.lang.ClassNotFoundException: mypackage.CloudwatchHandler
java.lang.ClassNotFoundException: mypackage.CloudwatchHandler
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
at java.logging/java.util.logging.LogManager.createLoggerHandlers(LogManager.java:1005)
at java.logging/java.util.logging.LogManager$4.run(LogManager.java:975)
at java.logging/java.util.logging.LogManager$4.run(LogManager.java:971)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
at java.logging/java.util.logging.LogManager.loadLoggerHandlers(LogManager.java:971)
at java.logging/java.util.logging.LogManager.initializeGlobalHandlers(LogManager.java:2424)
at java.logging/java.util.logging.LogManager$RootLogger.accessCheckedHandlers(LogManager.java:2526)
at java.logging/java.util.logging.Logger.getHandlers(Logger.java:2090)
at java.logging/java.util.logging.Logger.log(Logger.java:977)
at java.logging/java.util.logging.Logger.doLog(Logger.java:1007)
at java.logging/java.util.logging.Logger.log(Logger.java:1030)
at java.logging/java.util.logging.Logger.info(Logger.java:1803)
at mypackage.CloudwatchHandler.init(CloudwatchHandler.java:51)
... main ...
The really crazy thing about this exception is that the class causing the ClassNotFoundException is actually a caller in the current stack frame, as seen in the stack trace. So clearly it has been FOUND or it couldn't be running.
What's causing this and how can I fix it? I just want to load my own log handler.
Spring Boot version is 2.6.3.
ClassNotFoundException can occur if the Handler is not deployed to load in the system class loader as that is what the LogManager uses to find handlers.
Update your test case and try again:
public static void main(String[] args) throws Exception {
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(Thread.currentThread().getContextClassLoader());
System.out.println(CloudwatchHandler.class.getClassLoader());
//This is what CloudwatchHandler.init(); triggers
Class.forName(CloudwatchHandler.class.getName(), true, Thread.currentThread().getContextClassLoader());
//This is what the LogManager is doing
Class.forName(CloudwatchHandler.class.getName(), true, ClassLoader.getSystemClassLoader());
//Force load the root handlers.
Logger.getLogger("").getHandlers();
CloudwatchHandler.init();
SpringApplication.run(MyApp.class, args);
}
If it is that the handler is deployed in the context class loader and not in the system classloader then you need to change how you package the handler so it is visible to the system classloader. The java.util.logging.config.class option is one part of the LogManager that will try loading classes via context classloader which is what will be able to see your classes. For this option you move the contents of your init method to a new class and have the constructor perform the action. On the command line then set the value to the FQCN of the new config class.

Implementing different destinations in applications on the Tomcat server

Earlier this year I developed an implementation of the SAP JCO CustomDestinationProvider for one of my Spring MVC tomcat applications. In my application, I use this implementation to call a BAPI in my SAP R/3 system to retrieve data.
I am now working on a second Spring MVC tomcat application that I want to have call a BAPI in my SAP R/3 system to retrieve data. It will be a different BAPI that I will be calling, thus it will be different data that I will be retrieving. Since this is a different application calling a different BAPI, I want to use a different SAP system user in my configurations. This new application will be running on the same physical tomcat server as the first application.
My question is should I develop another implementation of the SAP JCO CustomDestinationProvider for this new application or should I somehow reuse the first implementation? If the answer is that I should develop another implementation for this new application, I would expect then that I would develop another implementation for each new Spring MVC tomcat application that I develop that needs to talk to SAP. Is this correct thinking?
If I do a different implementation for this new application of mine, should I be using the same destination name in the code, or should I use a different name?
Below is the code for my first implementation of CustomDestinationDataProvider:
public class CustomDestinationDataProvider {
public class MyDestinationDataProvider implements DestinationDataProvider {
private DestinationDataEventListener eL;
private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();
public Properties getDestinationProperties(String destinationName) {
try {
Properties p = secureDBStorage.get(destinationName);
if(p!=null) {
if(p.isEmpty())
throw new DataProviderException(DataProviderException.Reason.INVALID_CONFIGURATION, "destination configuration is incorrect", null);
return p;
}
return null;
} catch(RuntimeException re) {
throw new DataProviderException(DataProviderException.Reason.INTERNAL_ERROR, re);
}
}
public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {
this.eL = eventListener;
}
public boolean supportsEvents() {
return true;
}
public void changeProperties(String destName, Properties properties) {
synchronized(secureDBStorage) {
if(properties==null) {
if(secureDBStorage.remove(destName)!=null)
eL.deleted(destName);
} else {
secureDBStorage.put(destName, properties);
eL.updated(destName); // create or updated
}
}
}
}
public ArrayList<String> executeSAPCall(Properties connectProperties, ArrayList<String> partnumbers) throws Exception {
String destName = "ABAP_AS";
SAPDAO sapDAO = new SAPDAO();
ArrayList<MaterialBean> searchResults = new ArrayList<MaterialBean>();
MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
boolean destinationDataProviderRegistered = com.sap.conn.jco.ext.Environment.isDestinationDataProviderRegistered();
JCoDestination dest;
try {
if (!destinationDataProviderRegistered) {
com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
myProvider.changeProperties(destName, connectProperties);
}
} catch(IllegalStateException providerAlreadyRegisteredException) {
logger.error("executeSAPCall: providerAlreadyRegisteredException!");
}
try {
dest = JCoDestinationManager.getDestination(destName);
searchResults = sapDAO.searchSAP(dest, partnumbers);
} catch(JCoException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return searchResults;
}
}
If the answer is that I should not need to implement another CustomDestinationDataProvider for my second application, what other considerations do I need to keep in mind?
You can only register one DestinationDataProvider so the one you set must be able to handle both (or more) different connections. In order to do this, you need unique names for each connection, i.e. destName can't be the fixed value ABAP_AS, you need to create one for each connection.
Your current implementation of the provider looks good for me, but your method when calling the RFC is mixing the creation of the connection and the actual RFC-calling too much in my eyes. IMHO you should separate the former into its own method, so you can call it from other parts of your application to e.g. do other things than RFC-calling.
I've figured it out! I discovered two different ways to implement CustomDestinationDataProvider so that I could use multiple destinations.
Something that I did that helped out with both of my different solutions was change out the method in CustomDestinationDataProvider that instantiates the MyDestinationDataProvider inner class so that instead of returning ArrayList, it returns JCoDestination. I changed the name of this method from executeSAPCall to getDestination.
The first way that I discovered that allowed me to use multiple destinations, successfully changing out destinations, was to introduce a class variable for MyDestinationDataProvider so that I could keep my instantiated version. Please note that for this solution, the CustomDestinationDataProvider class is still embedded within my java application code.
I found that this solution only worked for one application. I was not able to use this mechanism in multiple applications on the same tomcat server, but at least I was finally able to successfully switch destinations. Here is the code for CustomDestinationDataProvider.java for this first solution:
public class CustomDestinationDataProvider {
private MyDestinationDataProvider gProvider; // class version of MyDestinationDataProvider
public class MyDestinationDataProvider implements DestinationDataProvider {
private DestinationDataEventListener eL;
private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();
public Properties getDestinationProperties(String destinationName) {
try {
Properties p = secureDBStorage.get(destinationName);
if(p!=null) {
if(p.isEmpty())
throw new DataProviderException(DataProviderException.Reason.INVALID_CONFIGURATION, "destination configuration is incorrect", null);
return p;
}
return null;
} catch(RuntimeException re) {
System.out.println("getDestinationProperties: Exception detected!!! message = " + re.getMessage());
throw new DataProviderException(DataProviderException.Reason.INTERNAL_ERROR, re);
}
}
public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {
this.eL = eventListener;
}
public boolean supportsEvents() {
return true;
}
public void changeProperties(String destName, Properties properties) {
synchronized(secureDBStorage) {
if(properties==null) {
if(secureDBStorage.remove(destName)!=null) {
eL.deleted(destName);
}
} else {
secureDBStorage.put(destName, properties);
eL.updated(destName); // create or updated
}
}
}
}
public JCoDestination getDestination(String destName, Properties connectProperties) {
MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
boolean destinationDataProviderRegistered = com.sap.conn.jco.ext.Environment.isDestinationDataProviderRegistered();
if (!destinationDataProviderRegistered) {
try {
com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
gProvider = myProvider; // save our destination data provider in the class var
} catch(IllegalStateException providerAlreadyRegisteredException) {
throw new Error(providerAlreadyRegisteredException);
}
} else {
myProvider = gProvider; // get the destination data provider from the class var.
}
myProvider.changeProperties(destName, connectProperties);
JCoDestination dest = null;
try {
dest = JCoDestinationManager.getDestination(destName);
} catch(JCoException e) {
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
}
return dest;
}
}
This is the code in my servlet class that I use to instantiate and call CustomDestinationDataProvider within my application code:
CustomDestinationDataProvider cddp = new CustomDestinationDataProvider();
SAPDAO sapDAO = new SAPDAO();
Properties p1 = getProperties("SAPSystem01");
Properties p2 = getProperties("SAPSystem02");
try {
JCoDestination dest = cddp.getDestination("SAP_R3_USERID_01", p1); // establish the first destination
sapDAO.searchEmployees(dest, searchCriteria); // call the first BAPI
dest = cddp.getDestination("SAP_R3_USERID_02", p2); // establish the second destination
sapDAO.searchAvailability(dest); // call the second BAPI
} catch (Exception e) {
e.printStackTrace();
}
Again, this solution only works within one application. If you implement this code directly into more than one application, the first app that calls this code gets the resource and the other one will error out.
The second solution that I came up with allows multiple java applications to use the CustomDestinationDataProvider class at the same time. I broke the CustomDestinationDataProvider class out of my application code and created a separate java spring application for it (not a web application) for the purpose of creating a jar. I then transformed the MyDestinationDataProvider inner class into a singleton. Here's the code for the singleton version of CustomDestinationDataProvider:
public class CustomDestinationDataProvider {
public static class MyDestinationDataProvider implements DestinationDataProvider {
////////////////////////////////////////////////////////////////////
// The following lines convert MyDestinationDataProvider into a singleton. Notice
// that the MyDestinationDataProvider class has now been declared as static.
private static MyDestinationDataProvider myDestinationDataProvider = null;
private MyDestinationDataProvider() {
}
public static MyDestinationDataProvider getInstance() {
if (myDestinationDataProvider == null) {
myDestinationDataProvider = new MyDestinationDataProvider();
}
return myDestinationDataProvider;
}
////////////////////////////////////////////////////////////////////
private DestinationDataEventListener eL;
private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();
public Properties getDestinationProperties(String destinationName) {
try {
Properties p = secureDBStorage.get(destinationName);
if(p!=null) {
if(p.isEmpty())
throw new DataProviderException(DataProviderException.Reason.INVALID_CONFIGURATION, "destination configuration is incorrect", null);
return p;
}
return null;
} catch(RuntimeException re) {
throw new DataProviderException(DataProviderException.Reason.INTERNAL_ERROR, re);
}
}
public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {
this.eL = eventListener;
}
public boolean supportsEvents() {
return true;
}
public void changeProperties(String destName, Properties properties) {
synchronized(secureDBStorage) {
if(properties==null) {
if(secureDBStorage.remove(destName)!=null) {
eL.deleted(destName);
}
} else {
secureDBStorage.put(destName, properties);
eL.updated(destName); // create or updated
}
}
}
}
public JCoDestination getDestination(String destName, Properties connectProperties) throws Exception {
MyDestinationDataProvider myProvider = MyDestinationDataProvider.getInstance();
boolean destinationDataProviderRegistered = com.sap.conn.jco.ext.Environment.isDestinationDataProviderRegistered();
if (!destinationDataProviderRegistered) {
try {
com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
} catch(IllegalStateException providerAlreadyRegisteredException) {
throw new Error(providerAlreadyRegisteredException);
}
}
myProvider.changeProperties(destName, connectProperties);
JCoDestination dest = null;
try {
dest = JCoDestinationManager.getDestination(destName);
} catch(JCoException ex) {
ex.printStackTrace();
throw ex;
} catch (Exception ex) {
ex.printStackTrace();
throw ex;
}
return dest;
}
}
After putting this code into the jar file application and creating the jar file (I call it JCOConnector.jar), I put the jar file on the shared library classpath of my tomcat server and restarted the tomcat server. In my case, this was /opt/tomcat/shared/lib. Check your /opt/tomcat/conf/catalina.properties file for the shared.loader line for the location of your shared library classpath. Mine looks like this:
shared.loader=\
${catalina.home}/shared/lib\*.jar,${catalina.home}/shared/lib
I also put a copy of this jar file in the "C:\Users\userid\Documents\jars" folder on my workstation so that the test application code could see the code in the jar and compile. I then referenced this copy of the jar file in my pom.xml file in both of my test applications:
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>jcoconnector</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>C:\Users\userid\Documents\jars\JCOConnector.jar</systemPath>
</dependency>
After adding this to the pom.xml file, I right clicked on each project, selected Maven -> Update Project..., and I then right clicked again on each project and selected 'Refresh'. Something very important that I learned was to not add a copy of JCOConnector.jar directly to either of my test projects. The reason for this is because I want the code from the jar file in /opt/tomcat/shared/lib/JCOConnector.jar to be used. I then built and deployed each of my test apps to the tomcat server.
The code that calls my JCOConnector.jar shared library in my first test application looks like this:
CustomDestinationDataProvider cddp = new CustomDestinationDataProvider();
JCoDestination dest = null;
SAPDAO sapDAO = new SAPDAO();
Properties p1 = getProperties("SAPSystem01");
try {
dest = cddp.getDestination("SAP_R3_USERID_01", p1);
sapDAO.searchEmployees(dest);
} catch (Exception ex) {
ex.printStackTrace();
}
The code in my second test application that calls my JCOConnector.jar shared library looks like this:
CustomDestinationDataProvider cddp = new CustomDestinationDataProvider();
JCoDestination dest = null;
SAPDAO sapDAO = new SAPDAO();
Properties p2 = getProperties("SAPSystem02");
try {
dest = cddp.getDestination("SAP_R3_USERID_02", p2);
sapDAO.searchAvailability(dest);
} catch (Exception ex) {
ex.printStackTrace();
}
I know that I've left out a lot of the steps involved in first getting the SAP JCO 3 library installed on your workstation and server. I do hope that this helps out at least one other person of getting over the hill of trying to get multiple spring mvc java spplications talking to SAP on the same server.

JBoss 5.1: Entity classes not found (vfszip)

I am using JBoss 5.1 with Hibernate 3.6, JPA 2.0 and Spring 3.0.5.
I use maven to build the EAR file which looks like :
AutoTrader.ear
-------> META-INF
--------------> application.xml
--------------> jboss-app.xml
--------------> MANIFEST.MF
-------> AutoTrader.war
if I deploy this ear file in JBoss 5.1, i get the error
org.springframework.dao.InvalidDataAccessApiUsageException: Not an entity: class uk.co.aol.shipmanager.domain.Manager; nested exception is ja
va.lang.IllegalArgumentException: Not an entity: class uk.co.aol.shipmanager.domain.Subscription
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:286) ~[at_war-1.0.war:3
.0.5.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:104) ~[at_war-1.0.war:3.0.5.RELEASE
]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:368) ~[at_war-1.
0.war:3.0.5.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58
) ~[at_war-1.0.war:3.0.5.RELEASE]
However, if I deploy the war file exploded, it works fine.
Any suggestions are welcome.
Thanks,
Adi
UPDATE:
I have added a ResourceScanner which extends NativeScanner:
public class ResourceScanner extends NativeScanner {
#Override
public Set<Class<?>> getClassesInJar(final URL jarToScan,
final Set<Class<? extends Annotation>> annotationsToLookFor) {
return super.getClassesInJar(patchUrl(jarToScan), annotationsToLookFor);
}
#Override
public Set<NamedInputStream> getFilesInJar(final URL jarToScan, final Set<String> filePatterns) {
return super.getFilesInJar(patchUrl(jarToScan), filePatterns);
}
#Override
public Set<Package> getPackagesInJar(final URL jarToScan,
final Set<Class<? extends Annotation>> annotationsToLookFor) {
return super.getPackagesInJar(patchUrl(jarToScan), annotationsToLookFor);
}
#Override
public String getUnqualifiedJarName(final URL jarToScan) {
return super.getUnqualifiedJarName(patchUrl(jarToScan));
}
/**
* Patch the VFS URL to a FILE protocol URL.
*
* #param url
* original URL.
* #return either the original, either the corresponding FILE protocol of given VFS URL.
*/
protected URL patchUrl(final URL url) {
String protocol = url.getProtocol();
if (protocol.equals("vfs")) {
try {
File file = new File(url.getFile());
return file.toURI().toURL();
} catch (final MalformedURLException e) {
return url;
} catch (IOException e) {
e.printStackTrace();
return url;
}
}
return url;
}
}
and, in spring-persistence.xml,
<property name="hibernate.ejb.resource_scanner" value="uk.co.aol.shipmanager.ResourceScanner"/>
This again works in the exploded war file.
But in case of a EAR file, the protocol is vfszip not vfs.
Please tell what to do???
did you tried to to use the following system parameter and see if it helped resolved the issue?
-Dorg.jboss.net.protocol.file.useURI=false

ClassCastException when using embedded glassfish for unit tests

I'm running some unit tests on some EJBS via maven and an embedded glassfish container. One of my tests works, but all subsequent attempts to test a different EJB result in the same error:
java.lang.ClassCastException: $Proxy81 cannot be cast to
Followed by whatever bean I'm attempting to test. I'm confident my setup is good since, as I say, one of my beans can be tested properly.
Examples of workiing code:
#Stateful
public class LayoutManagerBean implements LayoutManager {
private final Log LOG = LogFactory.getLog(LayoutManagerBean.class);
public List<Menu> getMenus(User currentUser) {
...
}
}
#Local
public interface LayoutManager {
public List<Menu> getMenus(User user);
}
And the test:
public class LayoutManagerTest {
private static EJBContainer ejbContainer;
private static Context ctx;
#BeforeClass
public static void setUp() {
ejbContainer = EJBContainer.createEJBContainer();
ctx = ejbContainer.getContext();
}
#AfterClass
public static void tearDown() {
ejbContainer.close();
}
#Test
public void getMenus() {
LayoutManager manager = null;
try {
manager = (LayoutManager) ctx.lookup("java:global/classes/LayoutManagerBean!uk.co.monkeypower.openchurch.core.layout.beans.LayoutManager");
} catch (NamingException e) {
System.out.println("Failed to lookup the gosh darned bean!");
}
assertNotNull(manager);
//Menu[] menus = manager.getMenus();
//assertTrue(menus.length > 1);
}
}
And an example of a failure:
#Singleton
public class OpenChurchPortalContext implements PortalContext {
private Set<PortletMode> portletModes = Collections.emptySet();
private Set<WindowState> windowStates = Collections.emptySet();
private Properties portalProperties = new Properties();
public OpenChurchPortalContext() {
portletModes.add(PortletMode.VIEW);
portletModes.add(PortletMode.HELP);
portletModes.add(PortletMode.EDIT);
portletModes.add(new PortletMode("ABOUT"));
windowStates.add(WindowState.MAXIMIZED);
windowStates.add(WindowState.MINIMIZED);
windowStates.add(WindowState.NORMAL);
}
...
}
And the test:
public class OpenChurchPortalContextTest {
private static EJBContainer ejbContainer;
private static Context ctx;
#BeforeClass
public static void setUp() {
ejbContainer = EJBContainer.createEJBContainer();
ctx = ejbContainer.getContext();
}
#AfterClass
public static void tearDown() {
ejbContainer.close();
}
#Test
public void test() {
OpenChurchPortalContext context = null;
try {
context = (OpenChurchPortalContext) ctx.lookup("java:global/classes/OpenChurchPortalContext");
} catch (NamingException e) {
System.out.println("Failed to find the bean in the emebedded jobber");
}
assertNotNull(context);
Set<PortletMode> modes = (Set<PortletMode>) context.getSupportedPortletModes();
assertTrue(modes.size() > 1);
Set<WindowState> states = (Set<WindowState>) context.getSupportedWindowStates();
assertTrue(states.size() > 1);
}
}
Any ideas as to why this may not be working?
You often get this problem if you are proxying a class, not an interface. Assuming that it's this line which is failing:
context = (OpenChurchPortalContext) ctx.lookup("java:global/classes/OpenChurchPortalContext");
OpenChurchPortalContext is a class, but it is being wrapped by a proxy class to implement the EJB specific functionality. This proxy class isn't a subclass of OpenChurchPortalContext, so you're getting a ClassCastException.
You aren't getting this with the first example, because the LayoutManager is an interface.
LayoutManager manager = null; // INTERFACE, so it works
try {
manager = (LayoutManager) ctx.lookup("java:global/classes/LayoutManagerBean!uk.co.monkeypower.openchurch.core.layout.beans.LayoutManager");
} catch (NamingException e) {
System.out.println("Failed to lookup the gosh darned bean!");
}
First, you can test to see if this is really your problem, change context to be a PortalContext not OpenChurchPortalContext:
PortalContext context = null;
try {
context = (PortalContext) ctx.lookup("java:global/classes/OpenChurchPortalContext");
} catch (NamingException e) {
System.out.println("Failed to find the bean in the emebedded jobber");
}
If your problem really is the Proxy, then the above code should work. If this is the case, you have two potential solutions:
When you do the ctx.lookup, always use an interface. This can be a bit of a pain, because you need to define an interface specifically for each EJB.
You may be able to configure your EJB container to proxy the classes instead of just the interfaces, similar to proxyTargetClass for Spring AOP. You'll need to check with the documentation for your container for that.
Your singleton EJB has a default local business interface by means of implementing PortalContext interface. The test client should know it only by its business interface, and the actual bean class (OpenChurchPortalContext) should not be referenced directly by the client. So the fix is to look it up by its business interface PortalContext.

Resources