I'm using Infinispan 13.0.0.final with the default marshaller (protobuf). When I try to use UUID fields in my datatypes
data class CounterState(
#get:ProtoField(number = 1) var index: Long? = null,
#get:ProtoField(number = 2) var uuid: UUID? = null
)
I get the following error at build time:
.../gradle-kotlin-protobuf/build/tmp/kapt3/stubs/main/io/radiosphere/ProtoSchema.java:8: error: org.infinispan.protostream.annotations.ProtoSchemaBuilderException: The class java.util.UUID must be instantiable using an accessible no-argument constructor.
public abstract interface ProtoSchema extends org.infinispan.protostream.GeneratedSchema {
It seems like I'm not allowed to use UUID in my types unless I generate a protoschema for it, but since UUID is a class outside of my control I can't do this.
Previous questions on the topic have gotten the suggestion to use the JavaSerializationMarshaller, but I want to solve this while still using the Protostream Marshaller. It has also been suggested that this would be fixed in version 12.0.0 here.
An example of this not working can be found here. Note that this project will not build because of the annotation processing failing as mentioned above. If it would build the proof that it is working would be shown by running the main project (ie. not the tests).
The question becomes: What do I need to do to configure UUID to be usable in my protobuf marshalled classes in Infinispan 13? Both for embedded and for a program using the hotrod client?
EDIT:
Based on a given answer I have also tried doing the following:
#AutoProtoSchemaBuilder(
includeClasses = [UUIDAdapter::class, CounterState::class],
schemaPackageName = "tutorial")
interface ProtoSchema : GeneratedSchema {
}
This makes the build work, but when starting Quarkus I get the following error:
Caused by: org.infinispan.protostream.DescriptorParserException: Duplicate type id 1005 for type org.infinispan.protostream.commons.UUID. Already used by tutorial.UUID
at org.infinispan.protostream.descriptors.ResolutionContext.checkUniqueTypeId(ResolutionContext.java:151)
at org.infinispan.protostream.descriptors.ResolutionContext.addGenericDescriptor(ResolutionContext.java:97)
at org.infinispan.protostream.descriptors.FileDescriptor.collectDescriptors(FileDescriptor.java:313)
at org.infinispan.protostream.descriptors.FileDescriptor.resolveDependencies(FileDescriptor.java:245)
at org.infinispan.protostream.descriptors.FileDescriptor.resolveDependencies(FileDescriptor.java:210)
at org.infinispan.protostream.descriptors.ResolutionContext.resolve(ResolutionContext.java:57)
at org.infinispan.protostream.impl.SerializationContextImpl.registerProtoFiles(SerializationContextImpl.java:127)
at org.infinispan.protostream.types.java.CommonTypesSchema.registerSchema(CommonTypesSchema.java:49)
at org.infinispan.client.hotrod.RemoteCacheManager.registerSerializationContextInitializer(RemoteCacheManager.java:422)
at org.infinispan.client.hotrod.RemoteCacheManager.registerDefaultSchemas(RemoteCacheManager.java:437)
at org.infinispan.client.hotrod.RemoteCacheManager.initializeProtoStreamMarshaller(RemoteCacheManager.java:409)
at org.infinispan.client.hotrod.RemoteCacheManager.actualStart(RemoteCacheManager.java:365)
at org.infinispan.client.hotrod.RemoteCacheManager.start(RemoteCacheManager.java:334)
at org.infinispan.client.hotrod.RemoteCacheManager.<init>(RemoteCacheManager.java:192)
at org.infinispan.client.hotrod.RemoteCacheManager.<init>(RemoteCacheManager.java:149)
at io.quarkus.infinispan.client.runtime.InfinispanClientProducer.initialize(InfinispanClientProducer.java:68)
If I instead change to use dependsOn like this:
#AutoProtoSchemaBuilder(
includeClasses = [CounterState::class],
dependsOn = [org.infinispan.protostream.types.java.CommonTypes::class, org.infinispan.protostream.types.java.CommonContainerTypes::class],
schemaPackageName = "tutorial")
I'm back to the build failing with:
error: org.infinispan.protostream.annotations.ProtoSchemaBuilderException: The class java.util.UUID must be instantiable using an accessible no-argument constructor.
public abstract interface ProtoSchema extends org.infinispan.protostream.GeneratedSchema {
It seems to be like Quarkus and the Annotation processor are getting in each others way here when it comes to having a simple working solution for UUID marshalling.
You have to include the org.infinispan.protostream.types.java.util.UUIDAdapter class in your annotation:
#AutoProtoSchemaBuilder(includeClasses = [CounterState::class, UUIDAdapter::class] , schemaPackageName = "tutorial")
For more info, check the documentation page.
I've seen many examples about Groovy objects as Spring beans but not vice versa. I'm using Groovy in a Java EE application like this:
GroovyCodeSource groovyCodeSource = new GroovyCodeSource(urlResource);
Class groovyClass = loader.parseClass(groovyCodeSource, false);
return (GroovyObject) groovyClass.newInstance();
In this way, classes written in Groovy with #Configurable annotation are being injected with Spring beans. It's OK for now.
How can I get the same by using GroovyScriptEngine? I don't want to define a class and I want it to work like a plain script. Is Spring/Groovy capable of that?
I've seen a post about this but I'm not sure whether it answers my question or not:
HERE
Do you mean that you'd like to add properties to the script, and inject those? Would you provide getter and setter? This does not make much sense to me. What makes sense, is adding the mainContext to the bindings of the script, or adding selected beans to the bindings.
These beans - or the context - would then be accessible directly in the script, as if it was injected.
def ctx = grailsApplication.mainContext
def binding = new Binding([:])
Map variables = [
'aService',
'anotherService'
].inject([config:grailsApplication.config, mainContext:ctx]) { m, beanName ->
def bean = ctx.getBean(beanName)
m[beanName] = bean
m
}
binding.variables << variables
def compiler = new CompilerConfiguration()
compiler.setScriptBaseClass(baseScriptClassName)
def shell = new GroovyShell(new GroovyClassLoader(), binding, compiler)
script=shell.parse(scriptStr)
script.binding=binding
script.init()
script.run()
We are using spring 3 and struts 2. We use spring #value annotation to get values from property files.
We want to get validation rules from property files instead of hard-coding them in action.
Here is sample property
system.properties
transfer.account.min.amount=10
Here is the action:
public class TransferToAccount implements Preparable {
#Value("${transfer.account.min.amount}") public String minAmount;
//...........execute and other methods omitted
#IntRangeFieldValidator(type = ValidatorType.FIELD, min = "${minAmount}", key = "validate.int.min")
public void setAmount(Integer amount) {
this.amount = amount;
}
The minAmount is populated correctly by value 10, but the validation is not working.
To see if parameters are passed correctly, I make a test as below.
Assume we want to get a key from spring managed property file ( This is just a test ;) )
system.properties
transfer.account.min.amount.key=validate.int.min
The resource bundle is:
validate.int.min = This field must be more than ${min}
...and we change validation as below:
#IntRangeFieldValidator(type = ValidatorType.FIELD, min = "${minAmount}", key = "${transfer.account.min.amount.key}")
Now when an error happens the validation message shows validate.int.min, instead of fetching this value from resource bundle!
Of course, when you run below code:
#IntRangeFieldValidator(type = ValidatorType.FIELD, min = "${minAmount}", key = "validate.int.min")
The error message is fetched resource bundle correctly!
If I can use annotation in this way, please let me know what is my mistake!
If I can not use annotations like this, please let me know what is the best way to avoid hard coding the validaiton rolls in actions.
I'm using NetBeans and Tomcat seems to be deploying in .war application twice, a double launch of the web app.
I've tried both Tomcat 6 and 7 and the same result.
I've got a Spring MVC, Hibernate and Thymeleaf application.
Context.xml under META-INF has the following content:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/website"/>
Here is the log.
**First deployment starts**
[ INFO] 07:13:09 ContextLoader - Root WebApplicationContext: initialization started
[ INFO] 07:13:09 XmlWebApplicationContext - Refreshing Root WebApplicationContext: startup date [Thu May 23 07:13:09 EST 2013]; root of context hierarchy
2013-05-23 07:13:10 JRebel: Monitoring Spring bean definitions in '/Users/pack/NetBeansProjects/mysite/site/src/main/webapp/WEB-INF/applicationContext- data.xml'.
[ INFO] 07:13:10 XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/applicationContext-data.xml]
[ INFO] 07:13:10 ClassPathScanningCandidateComponentProvider - JSR-330 'javax.inject.Named' annotation found and supported for component scanning
***(tomcat initializes hibernate and other spring beans)***
...
May 23, 2013 7:13:17 AM org.apache.catalina.startup.Catalina start
INFO: Server startup in 15552 ms
***Tomcat started***
***Tomcat tries to shut down the context***
[ INFO] 07:13:18 XmlWebApplicationContext - Closing WebApplicationContext for namespace 'spring-mvc-servlet': startup date [Thu May 23 07:13:15 EST 2013]; parent: Root WebApplicationContext
[ INFO] 07:13:18 DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#5bbe2de2: defining beans [blHeadProcessor,blHeadProcessorExtensionManager,navigationProcessor,blPaginationPageLinkPro cessor,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,blRegisterCustomerValidator,blCategoryController,com.package.ui.thymeleaf.CategoryHandlerMapping#0,templateResolver,templateEngine,org.thymeleaf.spring3.view.ThymeleafViewResolver#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory#521e7f21
[ INFO] 07:13:18 XmlWebApplicationContext - Closing Root WebApplicationContext: startup date [Thu May 23 07:13:09 EST 2013]; root of context hierarchy
[ INFO] 07:13:18 DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#521e7f21: defining beans [org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0,blCategoryDao,blCustomerDao,blIdGenerationDao,nlpDao,jpaTemplate,webDS,entityManagerFactory,transactionManager,org.springframework.security.filterChains,org.springframework.security.filterChainProxy,org.springframework.security.web.DefaultSecurityFilterChain#0,org.springframework.security.web.DefaultSecurityFilterChain#1,org.springframework.security.web.DefaultSecurityFilterChain#2,org.springframework.security.web.DefaultSecurityFilterChain#3,org.springframework.security.web.DefaultSecurityFilterChain#4,org.springframework.security.web.DefaultSecurityFilterChain#5,org.springframework.security.web.PortMapperImpl#0,org.springframework.security.web.PortResolverImpl#0,org.springframework.security.authentication.ProviderManager#0,org.springframework.security.web.context.HttpSessionSecurityContextRepository#0,org.springframework.security.web.savedrequest.HttpSessionRequestCache#0,org.springframework.security.web.access.channel.ChannelDecisionManagerImpl#0,org.springframework.security.access.vote.AffirmativeBased#0,org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0,org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator#0,org.springframework.security.authentication.AnonymousAuthenticationProvider#0,org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0,org.springframework.security.userDetailsServiceFactory,org.springframework.security.web.DefaultSecurityFilterChain#6,org.springframework.security.authentication.dao.DaoAuthenticationProvider#0,org.springframework.security.authentication.DefaultAuthenticationEventPublisher#0,org.springframework.security.authenticationManager,blUserDetailsService,blCatalogService,blCustomerService,entityService,fbPageService,blIdGenerationService,blLoginService,nlpService,priceIncreaseValidator,searchFacetService,blEntityConfiguration,blPasswordEncoder,solrIndexService,solrEmbedded,textEncryptor,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
[ INFO] 07:13:18 LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'blPU'
[ INFO] 07:13:18 SessionFactoryImpl - closing
May 23, 2013 7:13:18 AM org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
SEVERE: The web application [/website] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
May 23, 2013 7:13:19 AM org.apache.catalina.startup.HostConfig deleteRedeployResources
INFO: Undeploying context [/website]
May 23, 2013 7:13:19 AM org.apache.catalina.startup.HostConfig deployDescriptor
INFO: Deploying configuration descriptor /Users/pack/Servers/apache-tomcat- 7.0.34/conf/Catalina/localhost/website.xml
2013-05-23 07:13:23 JRebel: Monitoring Log4j configuration in 'file:/Users/pack/NetBeansProjects/mysite/site/target/mycompany/WEB-INF/classes/log4j.xml'.
***Tomcat tries to restart again***
[ INFO] 07:13:23 ContextLoader - Root WebApplicationContext: initialization started
[ INFO] 07:13:23 XmlWebApplicationContext - Refreshing Root WebApplicationContext: startup date [Thu May 23 07:13:23 EST 2013]; root of context hierarchy
2013-05-23 07:13:24 JRebel: Monitoring Spring bean definitions in '/Users/pack/NetBeansProjects/mysite/site/src/main/webapp/WEB-INF/applicationContext- data.xml'.
[ INFO] 07:13:24 XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/applicationContext-data.xml]
and it start successfully.
What I don't understand is why is Tomcat is deploying the application twice.
This does not occur the first time I deploy the application on a new tomcat instance because the website.xml file is not in tomcats /conf/catalina/localhost folder yet. But when I stop and run tomcat from netbeans again the website.xml file is still in the /conf/catalina/localhost folder but get deleted and re-deployed just before the second deployment is about to happen.
I've tried setting autoDeploy in server.xml file of Tomcat to false but it didn't help.
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="false">
May be Tomcat should be deleting the website.xml file under /conf/catalina/localhost folder everytime tomcat stops.
This is how website.xml file under localhost folder looks like
<?xml version="1.0" encoding="UTF-8"?>
<Context
docBase="/Users/pack/NetBeansProjects/mysite/site/target/mycompany"
path="/website"
/>
I found that deleting the file conf/localhost/myappname.xml prevents the app from initializing twice. Basically Tomcat is restarting, and restarting the old version of your app. Then it starts again when Netbeans deploys it. As a workaround, I added a few lines of code in my ContextListener contextDestroyed() event:
public void contextDestroyed(ServletContextEvent sce) {
...
String delme = sce.getServletContext().getInitParameter("eraseOnExit");
if (delme != null && delme.length() > 0) {
File del = new File(delme);
if (del.exists()) {
System.out.println("Deleting file " + delme);
del.delete();
}
}
In the web.xml add the following in a dev environment:
<context-param>
<description>Workaround for Tomcat starting webapp twice</description>
<param-name>eraseOnExit</param-name>
<param-value>/Users/xxx/apache-tomcat-7.0.42/conf/Catalina/localhost/myappname.xml</param-value>
</context-param>
Then the next time the app is deployed, it won't be started again prior to the deployment, hence will not start twice. Any other ideas for deleting a file prior to deploying, or on shutdown, would be appreciated.
Thanks for the answer by epoch and the answer by Steven Neiner.
Here is my version of their code. My differences:
Marked method as synchronized.
Theoretically not needed, but given that we are dealing with weird multiple launching problem here it is better to be safe than sorry.
Replaced calls to third-party utility (Spring?).
Detect if running in development by looking for certain wording in the catalina.base path.
Deleted the static modifier. Using a Singleton implemented as an enum.
As of 2016-05, rewrote the code to be easier to read and comprehend (at least for me). Only briefly tested, so be sure to review the source code before using (and must be used entirely at your own risk).
Concept
The core of their workaround to this bug is to delete the file named with the name of your web app (your “servlet context”) and appended with .xml.
For example, if your web app is named AcmeApp, locate and delete the file named AcmeApp.xml. This file is stored nested within the “Catalina base” folder.
Do this deletion as the very last step of your web app’s run. So when the web app launches again, that file will not exist, and will be recreated. Remember this is only in development mode. The bug does not occur when using Tomcat on its own in production.
So how do we run this workaround code as the last act of our web app’s execution? Read on.
How To Use
As a standard part of version 2.3 and later of the Servlet spec, every Servlet container has hooks to call your code when your web launches and again when your web app is being shut down. This is not Tomcat specific; Jetty, GlassFish, WildFly/JBoss, and so on, all include this feature as required by the Servlet spec.
To use the code shown above, add a new class to your project. Name the new class something like "MyServletContextListener.java". Declare that class as implementing the ServletContextListener interface.
Implement the two methods required by this interface. One method is called by your Servlet container (Tomcat) when your web app launches, guaranteed to run before the first user hits your app. The other method is called when your web app is being shut down by the Servlet container (Tomcat).
In the contextDestroyed method, call the method shown above. Like this:
#Override
public void contextInitialized ( ServletContextEvent sce )
{
// Web app launching.
// This method runs *before* any execution of this web app’s servlets and filters.
// Do nothing. No code needed here.
}
#Override
public void contextDestroyed ( ServletContextEvent sce )
{
// Web app shutting down.
// This method runs *after* the last execution of this web app’s servlets and filters.
// Workaround for NetBeans problem with launching Tomcat twice.
this.workaroundTomcatNetbeansRedeployBug( sce );
}
The configuration is easy. Merely include this class alongside your servlet class it a WAR file/folder. The #WebListener annotation causes the Servlet container to “notice” this listener class, load and instantiate it, and when appropriate execute each of its methods. You may use alternate modes of configuration instead of the annotation if needed, but the annotation is the simplest route.
Here is an entire AppListener class as a full example. I have re-written the previously posted version of this code to be easier to read and comprehend.
package com.basilbourque;
import java.io.File;
import java.io.FilenameFilter;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* Hooks into the web app launching and quitting, as a workaround for Tomcat
* running from NetBeans causing the web app to rapidly deploy, undeploy and
* redeploy.
*
* © 2016 Basil Bourque. This source code may be used freely, and entirely at
* your own risk, according to terms of the ISC License at:
* https://opensource.org/licenses/ISC
*
* #author Basil Bourque
*/
#WebListener
public class AppListener implements ServletContextListener {
#Override
public void contextInitialized ( final ServletContextEvent servletContextEventArg ) {
System.out.println ( "Basil launch" );
}
#Override
public void contextDestroyed ( final ServletContextEvent servletContextEventArg ) {
System.out.println ( "Basil exit" );
this.workaroundTomcatNetbeansRedeployBug ( servletContextEventArg );
}
synchronized private void workaroundTomcatNetbeansRedeployBug ( final ServletContextEvent servletContextEventArg ) {
// When running Tomcat 8 from NetBeans 8, as we do in development, a bug causes the web app to rapidly deploy, undeploy, and redeploy.
// This bug causes multiple bad side-effects.
//
// Workaround: When running in development mode with NetBeans & Tomcat, delete the XML file with name of web app, found in {catalina-base}/conf/Catalina/localhost/YourWebAppNameHere.xml.
// Example of file name to delete: If your app is named “AcmeApp”, then in a Vaadin multi-module Maven archetype app, the target file might be named “AcmeApp-ui.xml”.
// In a simpler project, the target file might be named “AcmeApp.xml”.
// So we need to determine the name of the web app in a soft-coded fashino.
// We extract from a context path. For example, '/AcmeApp-ui'. We need to remove that slash (SOLIDUS) at the front.
// Then we append a “.xml” to create our target file name.
// We look for that file in the folder nested in the Cataline base folder (see line above for path).
// If file is found, add it to the list of files to be deleted. That list will have only one element.
// Lastly, delete the file.
if ( AppUtility.INSTANCE.isInDevelopmentMode () ) { // Find a strategy to determine if you are in development mode.
final String catalinaBase = System.getProperty ( "catalina.base" );// Path to the folder the working folder of this web app.
final String contextPath = servletContextEventArg.getServletContext ().getContextPath ();
final String contextName = contextPath.substring ( 1 ); // Strip the SOLIDUS (slash) from first character position. Example: '/AcmeApp-ui' becomes 'AcmeApp-ui'.
final String fileNameToDelete = contextName + ".xml";
final File catalinaBaseContext = new File ( catalinaBase , "conf/Catalina/localhost" ); // While in development, running Tomcat from NetBeans, the web app’s name is 'localhost'.
if ( catalinaBaseContext.exists () && catalinaBaseContext.canRead () ) { // Confirm that we found the expected configuration folder nested in Catalina’s 'base' folder.
// Make an array of File objects that match our criterion of having one of our expected file names.
// Populate this array by defining a filter of filenames via a functional interface, to be applied against each file found in folder.
final File[] filesToDelete = catalinaBaseContext.listFiles ( new FilenameFilter () {
#Override
public boolean accept ( File dir , String name ) {
boolean accepting = ( name.equals ( fileNameToDelete ) );
return accepting;
}
} );
if ( filesToDelete.length == 0 ) { // If list of files is empty…
// FIXME Handle error. Should always find one file to delete.
System.out.println ( "ERROR - Found no file to delete as workaround for NetBeans+Tomcat double-launch bug. Expected file name: " + fileNameToDelete + " | Message # 42ec5857-9c1b-431a-b5c1-2588669a0ee2." );
return;
}
if ( filesToDelete.length > 1 ) { // If list of files has more than one file…
// FIXME Handle error. Should never find more than one file to delete.
System.out.println ( "ERROR - Found more than one file to delete as workaround for NetBeans+Tomcat double-launch bug." + " | Message # 0afbd6ca-3722-4739-81dc-b2916e9dbba4." );
return;
}
for ( File file : filesToDelete ) {
file.delete (); // Delete first file found in our filtered array.
// FIXME You may want to log this deletion.
System.out.println ( "TRACE - Deleting file as workaround for NetBeans+Tomcat double-launch bug: " + file + " | Message # 5a78416c-6653-40dc-a98c-6d9b64766d96." );
break; // Should be exactly one element in this list. But out of abundant caution, we bail-out of the FOR loop.
}
}
}
}
}
And here is the helper class to determine if running in development mode. Read the comments for more discussion. The upshot is that there seems to be no simple clean way to detect when in development, no way to detect when running Tomcat from NetBeans rather than running Tomcat on its own. I have asked but have not received any better solution.
CAVEAT: You must alter this isInDevelopmentMode method to match your particular development environment.
package com.basilbourque;
import java.util.ArrayList;
import java.util.List;
/**
* Detects if this web app is running in the Apache Tomcat web container from
* within NetBeans during development time.
*
* © 2016 Basil Bourque. This source code may be used freely, and entirely at
* your own risk, according to terms of the ISC License at:
* https://opensource.org/licenses/ISC
*
* #author Basil Bourque.
*/
public enum AppUtility {
INSTANCE;
transient private Boolean isDevMode;
synchronized public Boolean isInDevelopmentMode () {
// There is no simple direct way to detect if running in development.
// As a workaround, I use some facts specific to my running Tomcat from NetBeans while developing.
//
// The “Catalina base” is the folder used by Tomcat’s Catalina module to do the work of your servlets.
// The names of the folders in the path to that folder can be a clue about running in development.
//
// By default, the Catalina base folder is nested within Tomcat’s own folder.
//
// If you run NetBeans with a bundled Tomcat installation that path may contain the word “NetBeans”.
// At least this is the case on Mac OS X where that bundled Tomcat is stored within the NetBeans app (an app is actually a folder in Mac OS X).
//
// You mant to create your own folder to hold Tomcat’s “base” folder.
// I do this on my development machine. I create a folder named something like "apache-tomcat-base-dev" in my home folder.
// Nested inside that folder are additional folders for each version of Tomcat I may be using, such as 'base-8.0.33'.
// Since I do not use such a name on my production environment, I can example the path for that phrasing to indicate development mode.
//
if ( null == this.isDevMode ) { // Lazy-loading.
// Retrieve the folder path to the current Catalina base folder.
String catalinaBaseFolderPath = System.getProperty ( "catalina.base" );
this.isDevMode = Boolean.FALSE;
// Examine that path for certain wording I expect to occur only in development and never in production.
List<String> list = new ArrayList<> ();
list.add ( "Application Support" );// Specific to Mac OS X only.
list.add ( "NetBeans" );
list.add ( "apache-tomcat-base-dev" ); // My own name for an external folder to keep Catalina base separate, outside of NetBeans and Tomcat.
for ( String s : list ) {
if ( catalinaBaseFolderPath.contains ( s ) ) {
this.isDevMode = Boolean.TRUE;
break; // Bail-out of the FOR loop after first hit.
}
}
}
return this.isDevMode;
}
}
First of all, thanks Steven! Here is a more portable version of the fix:
/**
* tomcat workaround bug, in development mode, if tomcat is stopped and application is not un-deployed,
* the old application will start up again on startup, and then the new code will be deployed, leading
* to a the app starting two times and introducing subtle bugs, when this app is stopped and in dev mode
* remove the deployment descriptor from catalina base
*/
private static void preventTomcatNetbeansRedeployBug(final ServletContextEvent sce) {
final String contextPath = sce.getServletContext().getContextPath();
final String catalinaBase = System.getProperty("catalina.base");
if (StringUtil.checkValidity(contextPath, catalinaBase)
&& FrameworkContext.getInstance().isDevEnvironment()) {
final File catalinaBaseContext = new File(catalinaBase, "conf/Catalina/localhost");
if (catalinaBaseContext.exists() && catalinaBaseContext.canRead()) {
final File[] contexts = catalinaBaseContext.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return name.equals(contextPath.substring(1) + ".xml");
}
});
if (contexts != null && contexts.length > 0) {
LOG.info("Deleting core context[" + contexts[0].getAbsolutePath() + "] since we are in dev");
contexts[0].delete();
}
}
}
}
PS: substitute unknown references for your own version of it :)
Call this custom method from the contextDestroyed method of your ServletContextListener implementation.
In my case it was (after clicking 'run' in NetBeans):
deploy app in Tomcat (unnecessary)
undeploy app in Tomcat (unnecessary)
build app
deploy app in Tomcat
And I fixed that issue simply by cleaning project before running it - 'clean' -> 'run' :)
I try out the platform-core-1.0 rc5 Plugin to services by events. Now I write a service in the grails-plugin "listadmin":
package listadmin
class SECO_ListenService {
#grails.events.Listener(topic='getEntriesOfList', namespace='listadmin')
def getEntriesOfList(String intnalListName) {
println "SECO_ListenService"
def Liste aList = Liste.findByInternal_name(intnalListName)
return aList.eintrage.toList()
}
}
This service should return a list for dropdown in an other grails-plugin called "institutionadmin". I want to use this list of the service for a dropdown of a domain-model. I should mention that I use dynamic scaffolding. Now I try to call this event in the domain-model:
package institutionadmin
import org.springframework.dao.DataIntegrityViolationException
class Einrichtung {
Long einrichtungs_type
Long type_of_conzept
int anzahl_gruppen
int anzahl_kinder_pro_Gruppe
String offnungszeiten
static hasMany = [rooms : Raum]
static constraints = {
def aList = []
def reply = event(for:"listadmin", topic:"getEntriesOfList", data:"einrichtung_type").waitFor()
aList = reply.value.toList()
einrichtungs_type(inList: aList)
}
}
If I try to run this application i get the following error:
Caused by MissingMethodException: No signature of method: institutionadmin.Einrichtung.event() is applicable for argument types: (java.util.LinkedHashMap) values: [[for:listadmin, topic:testEventBus]]
Possible solutions: ident(), every(), every(groovy.lang.Closure), count(), get(java.io.Serializable), print(java.lang.Object)
If call this event in a controller everything is fine and the documentation of this plugin describe that I can call events also in domain-models and services... This error-method tell me, that the class don't know the event method.
Do I have to configure anything else?
Should call the event in another way or where is my mistake?
Has anybody experiences with this module?
The event(...) dynamic methods are not available on class (static) level.
You can pull the grailsEvents spring bean and call its event() method alternatively. You still have to get the bean from the application context statically though.
You could also use a custom validator instead, as you can get the current domain instance as a parameter, which should have the event() method injected.
something like this :
static myList = []
static constraints = {
einrichtungs_type validator: { value, instance ->
if(!myList){
// cache it the first time you save/validate the domain
// I would probably recommend you NOT to do this here though in
// real life scenario
def reply = instance.event('blabla').get()
myList = reply.value.toList()
}
return value in myList
}
}
Anyway, In my case I would probably load the list elsewhere (in the Bootstrap.groovy for instance) and use it / inject it in my domain instead of doing in the constraints closure.
I faced similar kind of problem, I wanted to use the event call inside a service class which is going to call the listener in other service class. When I started my application I got the same error.What I did was, added the plugin(platform-core:1.0.RC5) entries in BuildConfig.groovy like below
plugins {
build(":tomcat:$grailsVersion",
":platform-core:1.0.RC5") {
export = false
}
compile ':platform-core:1.0.RC5'
runtime ':platform-core:1.0.RC5'
}
Then I ran grails > clean and grails > compile on that project and restarted the server.It started working. Might be you can give a try.