JSPs with Spring Boot - spring-boot

I have been working on a spring-boot project initialized with spring initialzr. The generated package has no /webapp directory, hence had to add /webapp directory. I have read from spring documentation that spring detects static files from /static,resources. I have placed 3 different index.jsp to test which one gets displayed by my controller. Below are the code snippets.
Directory Tree:
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │   └── databasedisplay
│   │   │   └── app
│   │   │   ├── AppApplication.java
│   │   │   ├── config
│   │   │   │   ├── WebAppInitializer.java
│   │   │   │   └── WebConfig.java
│   │   │   ├── controller
│   │   │   │   ├── HomeController.java
│   │   │   │   └── IndexController.java
│   │   │   ├── repository
│   │   │   └── ServletInitializer.java
│   │   ├── resources
│   │   │   ├── application.properties
│   │   │   ├── index.jsp
│   │   │   ├── static
│   │   │   │   └── index.jsp
│   │   │   └── templates
│   │   └── webapp
│   │   └── WEB-INF
│   │   └── views
│   │   └── index.jsp
│   └── test
│   └── java
│   └── com
│   └── databasedisplay
│   └── app
│   └── AppApplicationTests.java
└── target
├── classes
│   ├── application.properties
│   ├── com
│   │   └── databasedisplay
│   │   └── app
│   │   ├── AppApplication.class
│   │   ├── config
│   │   │   ├── WebAppInitializer.class
│   │   │   └── WebConfig.class
│   │   ├── controller
│   │   │   ├── HomeController.class
│   │   │   └── IndexController.class
│   │   └── ServletInitializer.class
│   ├── index.jsp
│   └── static
│   └── index.jsp
├── generated-sources
│   └── annotations
├── generated-test-sources
│   └── test-annotations
├── maven-status
│   └── maven-compiler-plugin
│   ├── compile
│   │   └── default-compile
│   │   ├── createdFiles.lst
│   │   └── inputFiles.lst
│   └── testCompile
│   └── default-testCompile
│   ├── createdFiles.lst
│   └── inputFiles.lst
└── test-classes
└── com
└── databasedisplay
└── app
└── AppApplicationTests.class
index.jsp (in '/resources')
<html>
<head></head>
<body>
<h1>This is the body of the sample view in /resources</h1>
</body>
index.jsp (in '/static')
<html>
<head></head>
<body>
<h1>This is the body of the sample view in /static</h1>
</body>
</html>
index.jsp (in '/WEB-INF/views/')
<html>
<head></head>
<body>
<h1>This is the body of the sample view in WEB-INF/views</h1>
</body>
</html>
Controller
#Controller
public class IndexController {
#RequestMapping(value = "/indexA", method = RequestMethod.GET)
public String index() {
return "index";
}
}
Configuration Classes
WebConfig.java
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver
= new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable("testServlet");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/").setCachePeriod(3600)
.resourceChain(true).addResolver(new PathResourceResolver());
}
}
WebInitializer.java
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.scan("com.databasedisplay.app");
container.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = container.addServlet("mvc", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
The issue is when I run with mvn spring-boot:run or mvn clean package spring-boot:run the \target directory as shown in the directory tree has no index.jsp from \WEB-INF\views\ (in fact the target directory doesn't have a WEB-INF directory at all). But I still get the following output when I curl http://localhost:8080/indexA :
This is the body of the sample view in WEB-INF/views
Can someone explain how the view resolver even maps the view names to the corresponding views? (I have looked into InternalResourceViewResolver and how to set prefixes and suffixes but that still does not explain how it can render a jsp when it is not in target)
Can someone point out to the differences between mvn spring-boot:run and mvn clean package spring-boot:run as in the latter there is WEB-INF directory in the target.
Why am I getting index.jsp corresponding to /WEB-INF but not other views on curl?

JSP's are part of legacy Java Enterprise applications and are not the core of Spring Boot/MVC and it's more modern Template Engine approach (although Spring is based on Java EE). If you have any good reason to use JSP's, it could work. But with Spring, the MVC approach/implementation is to use the more modern template engines with technologies like Thymeleaf, FreeMarker, Groovy Markup, and Mustache.
Question 1 If you have configured your pom.xml correctly, you can use different starters to configure how your application is deployed/run. JSP's are not a standard solution for Spring and should be configured seperately, it needs to be configured so that it will compile the JSP's into their respective location so that Tomcat reads it from the webapps folder. To compile and render JSP's your pom.xml needs both spring-boot-starter-web and tomcat-embed-jasper including the tag <scope>provided</scope>.
Question 2 Spring comes with an embedded Tomcat server (spring-boot-starter-web). When you run mvn spring-boot:run it will start a Tomcat server and deploy your Spring Boot application on the Tomcat localhost:8080. mvn clean, before spring-boot:run, just deletes the output of a build by deleting the build directory.
Question 3 Each .HTML template or .JSP file have their respective location in the project before compiling, so therefore some .JSP's are not compiled in your folders.
A Java Enterprise application and it's corresponding JSP's use a different project structure than Spring: All JSP's will be compiled from the "src/main/webapp/WEB-INF/jsp/" if you have the right dependencies and run spring-boot:run. If you manually create a project, through compiling by cmd -jar, you should include your JSP's in the "/webapp/" folder and the Java.classes in WEB-INF/classes/MyServlet.class.
By using, for instance Thymeleaf (spring-boot-starter-thymeleaf), if you build your artifacts the IDE will compile templates from /resources/templates and work from there on your MVC project, where you can integrate your REST controllers seamlessly.
Tomcat stays pivotal in how your enterprise application is deployed, only that you need to adjust your project in Spring so that it will map and compile the right files into the .WAR before deploying.
My advice would be, for MVC, to use the template engines instead of the legacy JSP's. of course, there will use cases for JSP's inside a Spring project but it requires a differect structure and dependencies.

Related

How to include another module in dependency of my gradle project, but exclude certain directories

We have a gradle multi module project. api-1 is a dropwizard legacy project, and api-2 is the spring-boot project.
root-project
├── api-1
│   ├── build.gradle
│   ├── config
│   ├── libs
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   └── resources
│   │   │   ├── META-INF
│   │   │      └── services
│   │   │      └── org.hibernate.integrator.spi.Integrator
├── api-2
│   ├── build.gradle
│   └── src
│   ├── main
│   │   └── resources
│   └── test
│   └── resources
├── gradle
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
We have our entities in api-1 which we are trying to use in api-2. So in api-2's build.gradle file I included implementation project(':api-2'). It feels a lil bit coarse to import the module along with all its transitive dependencies in to this module; but for now I does the trick.
The Issue:
The problem here is that api-1 has hibernate 5.2 dependency and it has this org.hibernate.integrator.spi.Integrator file in the resource folder. It references org.hibernate.jpa.event.spi.JpaIntegrator. Apparently this class was removed from hibernate from version 5.3 (ref: https://github.com/hibernate/hibernate-orm/blob/5.3/migration-guide.adoc#jpaintegrator-removed )
Now when I run (task bootRun) my api-2 module, I it fails with the below exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.util.ServiceConfigurationError: org.hibernate.integrator.spi.Integrator: Provider org.hibernate.jpa.event.spi.JpaIntegrator not found
What I Already Tried:
However if I delete this file (org.hibernate.integrator.spi.Integrator) from api-2, then api-2 runs and also api-1 seems to be running without any issues. But api-1 is a legacy project, and I am wary that it is wreck-less to simply remove the file without understanding it's implication fully.
I am trying to figure out if there is a safer way to get around this issue; maybe exclued that particular file in api-2. Or any better alternative?

Entities not scanned

I have a Maven project that represents the data layer of a single sign on (SSO) server. Other client applications shall rely on it for user authentication and authorization.
It is a .jar archive that shall be used by another REST project, also a Maven project.
Both are Spring Boot 2 based projects.
This project shall have some integration tests against the H2 and the MySQL databases.
As it is a library it does not run on its own. But it has some integration tests, that are to be executed. There is therefore no main class.
The command being used to build and execute the tests is mvn clean install -Denv="test"
My tree of source code looks like:
├── pom.xml
└── src
├── main
│   ├── java
│   │   └── com
│   │   └── thalasoft
│   │   └── userdata
│   │   ├── config
│   │   │   ├── DatabaseConfiguration.java
│   │   │   ├── JpaService.java
│   │   │   └── properties
│   │   │   ├── AbstractDatabaseProperties.java
│   │   │   ├── DatabaseH2TestProperties.java
│   │   │   ├── DatabaseMySQLAcceptanceProperties.java
│   │   │   ├── DatabaseMySQLPreProdProperties.java
│   │   │   ├── DatabaseMySQLProdProperties.java
│   │   │   ├── DatabaseMySQLTestProperties.java
│   │   │   ├── DatabaseOraclePreProdProperties.java
│   │   │   ├── DatabaseOracleProdProperties.java
│   │   │   ├── DatabaseOracleTestProperties.java
│   │   │   ├── DatabaseProperties.java
│   │   │   └── PropertyNames.java
│   │   ├── dialect
│   │   │   ├── CustomMySQL5InnoDBDialect.java
│   │   │   └── CustomOracle10gDialect.java
│   │   ├── exception
│   │   │   ├── CannotDeleteEntityException.java
│   │   │   ├── EnrichableException.java
│   │   │   ├── EntityAlreadyExistsException.java
│   │   │   ├── EntityNotFoundException.java
│   │   │   └── NoEntitiesFoundException.java
│   │   ├── jpa
│   │   │   ├── domain
│   │   │   │   ├── AbstractEntity.java
│   │   │   │   ├── EmailAddress.java
│   │   │   │   ├── User.java
│   │   │   │   └── UserRole.java
│   │   │   └── repository
│   │   │   ├── GenericRepositoryImpl.java
│   │   │   ├── GenericRepository.java
│   │   │   ├── UserRepositoryCustom.java
│   │   │   ├── UserRepositoryImpl.java
│   │   │   ├── UserRepository.java
│   │   │   ├── UserRoleRepositoryCustom.java
│   │   │   ├── UserRoleRepositoryImpl.java
│   │   │   └── UserRoleRepository.java
│   │   └── service
│   │   ├── UserRoleServiceImpl.java
│   │   ├── UserRoleService.java
│   │   ├── UserServiceImpl.java
│   │   └── UserService.java
│   └── resources
│   ├── application.properties
│   └── custom
│   └── typedef.hbm.xml
└── test
├── java
│   └── com
│   └── thalasoft
│   └── userdata
│   ├── assertion
│   │   └── UserAssert.java
│   ├── it
│   │   ├── jpa
│   │   │   ├── AbstractRepositoryTest.java
│   │   │   ├── UserRepositoryTest.java
│   │   │   └── UserRoleRepositoryTest.java
│   │   └── service
│   │   ├── AbstractServiceTest.java
│   │   └── UserServiceTest.java
│   └── ut
│   ├── AbstractRepositoryTest.java
│   └── UserRepositoryTest.java
└── resources
├── h2
│   └── data-source-test.properties
├── mysql
│   ├── clean-up-before-each-test.sql
│   └── data-source-test.properties
└── oracle
├── data-source-preprod.properties
└── data-source-test.properties
The DatabaseH2TestProperties is loaded thanks to a custom annotation define in another Maven toolbox project:
#EnvTest
#DbH2
#Configuration
#PropertySource({ "classpath:h2/data-source-test.properties" })
public class DatabaseH2TestProperties extends AbstractDatabaseProperties {
private static Logger logger = LoggerFactory.getLogger(DatabaseH2TestProperties.class);
public DatabaseH2TestProperties() {
logger.debug("===========>> Loading the classpath h2/data-source-test.properties file");
}
}
The data-source-test.properties file contains:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.datasource.driver-class-name=net.sf.log4jdbc.DriverSpy
spring.datasource.url=jdbc:log4jdbc:h2:file:./target/useraccounttest
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
The tests all are based on the class:
#ContextConfiguration(classes = { DatabaseConfiguration.class })
#RunWith(SpringRunner.class)
#Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, scripts = { "classpath:mysql/clean-up-before-each-test.sql" })
public abstract class AbstractRepositoryTest {
}
On this class, I wonder if I should not use #SpringBootTest(classes = { DatabaseConfiguration.class }) instead.
And the configuration is done with:
#EnableAutoConfiguration
#ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.userdata" })
public class DatabaseConfiguration {
}
The connection to the H2 database is successful:
02:23:56.299 [main] DEBUG jdbc.audit - 100. Connection.getMetaData() returned dbMeta74: conn99: url=jdbc:h2:file:./target/useraccounttest user=SA com.zaxxer.hikari.pool.ProxyConnection.getMetaData(ProxyConnection.java:361)
02:23:56.299 [main] DEBUG org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator - Database ->
name : H2
version : 1.4.197 (2018-03-18)
major : 1
minor : 4
02:23:56.299 [main] DEBUG org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator - Driver ->
name : H2 JDBC Driver
version : 1.4.197 (2018-03-18)
major : 1
minor : 4
02:23:56.299 [main] DEBUG org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator - JDBC version : 4.0
02:23:56.299 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
02:23:56.305 [main] DEBUG jdbc.audit - 100. Connection.clearWarnings() returned com.zaxxer.hikari.pool.ProxyConnection.close(ProxyConnection.java:250)
But I get an excetpion.
What the console log has to say:
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'com.thalasoft.userdata.config.JpaService'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'com.thalasoft.userdata.config.properties.DatabaseH2TestProperties'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.thalasoft.userdata.jpa.repository.GenericRepositoryImpl'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'com.thalasoft.userdata.jpa.repository.GenericRepositoryImpl'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
02:23:56.340 [main] WARN org.springframework.context.support.GenericApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.thalasoft.userdata.jpa.repository.GenericRepositoryImpl' defined in file [/home/stephane/dev/java/projects/user-data/target/classes/com/thalasoft/userdata/jpa/repository/GenericRepositoryImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.Class<?>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Indeed, I have a custom generic repository:
#Repository
#Transactional
public class GenericRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
implements GenericRepository<T, ID> {
private EntityManager entityManager;
private final Class<T> domainClass;
public GenericRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
this.domainClass = domainClass;
}
public EntityManager getEntityManager() {
return entityManager;
}
}
#NoRepositoryBean
public interface GenericRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
public EntityManager getEntityManager();
}
And it is used as:
public interface UserRepository extends GenericRepository<User, Long>, UserRepositoryCustom {
}
public interface UserRepositoryCustom {
public User deleteByUserId(Long id) throws EntityNotFoundException;
}
public class UserRepositoryImpl implements UserRepositoryCustom {
#Autowired
private UserRepository userRepository;
}
It feels like the domain class cannot be found for the generic type.
Your GenericRepository interface and GenericRepositoryImpl class fit the pattern for repository fragment used in custom repository implementations in Spring Data JPA, so my guess is that Spring Data is trying to instantiate it for use as such. I would try renaming GenericRepositoryImpl to AbstractGenericRepository or making it an abstract class or perhaps both. You could also try removing #Repository annotation - after all class GenericRepositoryImpl is not a repository, but rather a base for implementing one.
Edit:
From your code it seems you're using the GenericRepository to create repository with custom method for eleting user by id. That being said I belive this code would be sufficient:
public interface UserRepository extends JpaRepository<User, Long> {
void deleteByUserId(Long id);
}
and if field userId in your User class is the one annotated as entity id, you do not even need the custom method since JpaRepository extends CrudRepository, which already has method deleteById(ID id).

How should the gradle build scripts be to modularize this application?

I am trying to apply an example from the book Gradle in Action to learn how to separate a gradle application into subprojects. I did the example in the book and everything worked out fine.
I decided to apply the same concepts to a sample application from the Griffon Framework called minimalistic build, source code: sources. I selected this application because it follows the normal application structure instead of Griffon's, and I was trying to fill the gap (IMHO) in Griffon's documentation for multi project builds: it has one exaple that uses Open-Dolphin, Swing, JavaFX, Servlets that I felt it was too complicated to learn the basics.
Of course I hit the wall big time, multiple times...
Griffon's JavaFX application has the following structure:
├── build.gradle
├── config
│   └── HEADER
├── gradle
│   ├── functional-test.gradle
│   ├── integration-test.gradle
│   ├── javafx-plugin.gradle
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── src
├── functional-test
│   └── java
│   └── org
│   └── example
│   └── SampleFunctionalTest.java
├── integration-test
│   └── java
│   └── org
│   └── example
│   └── SampleIntegrationTest.java
├── main
│   ├── java
│   │   ├── Config.java
│   │   └── org
│   │   └── example
│   │   ├── Launcher.java
│   │   ├── SampleController.java
│   │   ├── SampleModel.java
│   │   ├── SampleService.java
│   │   └── SampleView.java
│   └── resources
│   ├── application.properties
│   ├── griffon-icon-128x128.png
│   ├── griffon-icon-16x16.png
│   ├── griffon-icon-24x24.png
│   ├── griffon-icon-256x256.png
│   ├── griffon-icon-32x32.png
│   ├── griffon-icon-48x48.png
│   ├── griffon-icon-64x64.png
│   ├── griffon.png
│   ├── log4j.properties
│   ├── messages.properties
│   ├── META-INF
│   │   └── griffon
│   │   ├── griffon.core.artifact.GriffonController
│   │   ├── griffon.core.artifact.GriffonModel
│   │   ├── griffon.core.artifact.GriffonService
│   │   └── griffon.core.artifact.GriffonView
│   ├── org
│   │   └── example
│   │   └── sample.fxml
│   └── resources.properties
└── test
└── java
└── org
└── example
├── SampleControllerTest.java
└── SampleServiceTest.java
The build.gradle file content is:
// tag::plugins[]
plugins {
id 'java'
id 'idea'
id 'com.github.ben-manes.versions' version '0.12.0'
id 'com.github.hierynomus.license' version '0.11.0'
}
apply from: 'gradle/javafx-plugin.gradle'
apply from: 'gradle/integration-test.gradle'
apply from: 'gradle/functional-test.gradle'
// end::plugins[]
// tag::javafx[]
javafx {
mainClass = 'org.example.Launcher'
}
// end::javafx[]
// tag::dependencies[]
repositories {
jcenter()
mavenLocal()
}
dependencies {
compile "org.codehaus.griffon:griffon-javafx:${griffonVersion}"
compile "org.codehaus.griffon:griffon-guice:${griffonVersion}"
runtime('log4j:log4j:1.2.17') {
exclude group: 'ant', module: 'ant-nodeps'
exclude group: 'ant', module: 'ant-junit'
exclude group: 'ant-contrib', module: 'ant-contrib'
}
runtime 'org.slf4j:slf4j-log4j12:1.7.21'
testCompile "org.codehaus.griffon:griffon-javafx-test:${griffonVersion}"
testCompile 'pl.pragmatists:JUnitParams:1.0.5'
testCompile 'org.mockito:mockito-core:2.0.59-beta'
}
// end::dependencies[]
// tag::resources[]
processResources {
from(sourceSets.main.resources.srcDirs) {
exclude '**/*.properties'
exclude '**/*.xml'
}
from(sourceSets.main.resources.srcDirs) {
include '**/*.properties'
include '**/*.xml'
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [
'application.name' : project.name,
'application.version': project.version,
'griffon.version' : griffonVersion
])
}
}
// end::resources[]
license {
header = rootProject.file('config/HEADER')
strictCheck = true
ignoreFailures = true
mapping {
java = 'SLASHSTAR_STYLE'
fxml = 'XML_STYLE'
}
ext.year = '2016'
exclude '**/*.png'
}
And the structure I was trying to achieve is:
├── build.gradle
├── config
│   └── HEADER
├── gradle
│   ├── functional-test.gradle
│   ├── integration-test.gradle
│   ├── javafx-plugin.gradle
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── launcher
│   ├── launcher-build.gradle
│   └── src
│   └── main
│   └── java
│   ├── Config.java
│   └── org
│   └── example
│   └── Launcher.java
├── resources
│   ├── resources-build.gradle
│   └── src
│   └── main
│   └── resources
│   ├── application.properties
│   ├── griffon-icon-128x128.png
│   ├── griffon-icon-16x16.png
│   ├── griffon-icon-24x24.png
│   ├── griffon-icon-256x256.png
│   ├── griffon-icon-32x32.png
│   ├── griffon-icon-48x48.png
│   ├── griffon-icon-64x64.png
│   ├── griffon.png
│   ├── log4j.properties
│   ├── messages.properties
│   └── resources.properties
├── service
│   ├── resources
│   │   └── META-INF
│   │   └── griffon
│   │   └── griffon.core.artifact.GriffonController
│   ├── service-build.gradle
│   └── src
│   ├── main
│   │   └── java
│   │   └── org
│   │   └── example
│   │   └── SampleService.java
│   └── test
│   └── java
│   └── org
│   └── example
│   └── SampleServiceTest.java
├── settings.gradle
└── view
├── src
│   ├── functional-test
│   │   └── java
│   │   └── org
│   │   └── example
│   │   └── SampleFunctionalTest.java
│   ├── integration-test
│   │   └── java
│   │   └── org
│   │   └── example
│   │   └── SampleIntegrationTest.java
│   ├── main
│   │   ├── java
│   │   │   └── org
│   │   │   └── example
│   │   │   ├── SampleController.java
│   │   │   ├── SampleModel.java
│   │   │   └── SampleView.java
│   │   └── resources
│   │   ├── META-INF
│   │   │   └── griffon
│   │   │   ├── griffon.core.artifact.GriffonModel
│   │   │   ├── griffon.core.artifact.GriffonService
│   │   │   └── griffon.core.artifact.GriffonView
│   │   └── org
│   │   └── example
│   │   └── sample.fxml
│   └── test
│   └── java
│   └── org
│   └── example
│   └── SampleControllerTest.java
└── view-build.gradle
I don't know if that structure is the one that makes more sense, but is the one I first thought of.
Regardless everything I tried I could not build this project and I am wondering if anyone could tell me if I made a mistake with the selection of the subprojects and what should be the content for the build files.
I created a zip file with the rearranged source here keeping the original build.gradle file untouched, with a settings.gradle file that makes Gradle properly recognize the new structure.
Thanks!
The problem is caused by missing resource files in the service project. You'll find a similar problem in the view project. This is is because all resource files were moved to another location (the resources project). There was no problem before splitting the application into subprojects because all resources were at the right place.
You can fix the build by following these steps:
$ cd service
$ mkdir -p src/test/resources
$ touch src/test/resources/Config.properties
$ mkdir -p src/main/resources
$ mv ../resources/src/main/resources/messages.properties src/main/resources/
$ cd ../view
$ mkdir -p src/test/resources
$ touch src/test/resources/Config.properties
This should be enough to make the tests green again.
Marcelo,
I've posted a new version of the multi-project build here. This version keeps common setup at the root level, leaving what's specific to each subproject on each subproject's build file.

Gradle plugin packaging - Why is plugin unexpectedly applied?

So I have:
buildSrc/
├── build.gradle
└── src
├── main
│   ├── groovy
│   │   └── build
│   │   ├── ExamplePlugin.groovy
│   │   └── ExampleTask.groovy
│   └── resources
│   └── META-INF
│   └── gradle-plugins
│   └── build.ExamplePlugin.properties
└── test
└── groovy
└── build
├── ExamplePluginTest.groovy
└── ExampleTaskTest.groovy
Question:
It seems like build.ExamplePlugin.properties maps directly to the build.ExamplePlugin.groovy. Is this the case? Seems terribly inefficient to have only one property in the file. Does it have to be fully qualified, i.e. does the name have to exactly match the full qualification of the class?
Now in the example, I see:
project.pluginManager.apply 'build.ExamplePlugin'
...however if I have that in my test, I get an error to the effect that the simple task the plugin defines, is already defined.
Why bother with test examples that require 'apply' when that is inappropriate for packaging?

Adding OSGi fragment bundles in Felix servletbridge war

I am trying to create war hosting OSGi bundles. The complete configuration should be able to host WAB bundles, and now I try to integrate pax-web-extender-war for that.
It requires some dependencies like slf4j-api and slf4j-log4j12 and here is the problem: I always get this exception:
org.osgi.framework.BundleException: Fragment bundles can not be started.
at org.apache.felix.framework.Felix.startBundle(Felix.java:1782)
because slf4j-log4j12 is really a fragment bundle. I assumed Felix should cope with this but it does not. So I tried to move this jar to WEB-INF/lib but then wiring fails as osgi cannot resolve it as a bundle.
So,
Where should I put fragment bundles ?
Should it be somehow configured in framework.properties ?
is Apache Felix even capable of working with fragment bundles ?
Following is current layout of the war (note that it is based on felix http bridge sample):
.
└── WEB-INF
├── bundles
│   ├── commons-fileupload-1.2.2.jar
│   ├── commons-io-2.4.jar
│   ├── hello-wab-1-SNAPSHOT.war
│   ├── org.apache.felix.http.bridge-2.2.0.jar
│   ├── org.apache.felix.http.samples.filter-2.2.0.jar
│   ├── org.apache.felix.webconsole-4.0.0.jar
│   ├── pax-web-api-2.1.0.jar
│   ├── pax-web-extender-war-2.1.0.jar
│   ├── pax-web-spi-2.1.0.jar
│   ├── slf4j-api-1.6.6.jar
│   ├── slf4j-log4j12-1.6.6.jar
│   └── wrapper-json-1-SNAPSHOT.jar
├── classes
│   └── org
│   └── apache
│   └── felix
│   └── http
│   └── samples
│   └── bridge
│   ├── FrameworkService.class
│   ├── ProvisionActivator.class
│   └── StartupListener.class
├── framework.properties
├── lib
│   ├── org.apache.felix.framework-4.0.3.jar
│   ├── org.apache.felix.http.proxy-2.2.0.jar
│   ├── org.apache.felix.webconsole-4.0.0.jar
│   ├── org.osgi.compendium-4.3.0.jar
│   └── wrapper-json-1-SNAPSHOT.jar
└── web.xml
I suggest adding pax-logging to your bundles, it'll keep away the pain of logging in the OSGi world. Pax-Logging
For the fragment-bundles you just need to add them to the usual bundles. I'd say in your setup probably in the bundles folder. Since it's a fragment bundle the Exception you get is right, it's not a "startable" bundle. It will only resolve and hopefully attached to the hosting bundle.
By the way, Felix is capable of working with fragment bundles :)

Resources