diff --git a/.gitignore b/.gitignore index 40f55760b0..204ca95f52 100755 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ view/target *.log fitnesse/target/ daisy-integration/target/ +war/target/ +api/target/ diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 0000000000..35c607e1b1 --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>SciPro</artifactId> + <version>0.1-SNAPSHOT</version> + </parent> + + <artifactId>api</artifactId> + + <dependencies> + <dependency> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>core</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-webmvc</artifactId> + </dependency> + </dependencies> + +</project> \ No newline at end of file diff --git a/view/src/main/java/se/su/dsv/scipro/api/ApiController.java b/api/src/main/java/se/su/dsv/scipro/api/ApiController.java similarity index 100% rename from view/src/main/java/se/su/dsv/scipro/api/ApiController.java rename to api/src/main/java/se/su/dsv/scipro/api/ApiController.java diff --git a/core/src/main/java/se/su/dsv/scipro/oauth/OAuthSettings.java b/core/src/main/java/se/su/dsv/scipro/oauth/OAuthSettings.java index a8b41b8648..2a20bcf204 100644 --- a/core/src/main/java/se/su/dsv/scipro/oauth/OAuthSettings.java +++ b/core/src/main/java/se/su/dsv/scipro/oauth/OAuthSettings.java @@ -1,4 +1,4 @@ package se.su.dsv.scipro.oauth; -record OAuthSettings(String uri, String redirectUri, String clientId, String clientSecret) { +public record OAuthSettings(String uri, String redirectUri, String clientId, String clientSecret) { } diff --git a/core/src/main/java/se/su/dsv/scipro/profiles/CurrentProfile.java b/core/src/main/java/se/su/dsv/scipro/profiles/CurrentProfile.java index 679ba53157..6bf6f2bd19 100755 --- a/core/src/main/java/se/su/dsv/scipro/profiles/CurrentProfile.java +++ b/core/src/main/java/se/su/dsv/scipro/profiles/CurrentProfile.java @@ -1,14 +1,10 @@ package se.su.dsv.scipro.profiles; -import jakarta.inject.Inject; -import jakarta.inject.Named; - public class CurrentProfile { private String currentProfileString; - @Inject - public void setCurrentProfileString(@Named("profile") String currentProfileString) { + public void setCurrentProfileString(String currentProfileString) { this.currentProfileString = currentProfileString; } diff --git a/core/src/main/java/se/su/dsv/scipro/workerthreads/AbstractWorker.java b/core/src/main/java/se/su/dsv/scipro/workerthreads/AbstractWorker.java index 751b6c8831..352c674419 100755 --- a/core/src/main/java/se/su/dsv/scipro/workerthreads/AbstractWorker.java +++ b/core/src/main/java/se/su/dsv/scipro/workerthreads/AbstractWorker.java @@ -1,6 +1,5 @@ package se.su.dsv.scipro.workerthreads; -import com.google.inject.persist.UnitOfWork; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,7 +24,6 @@ public abstract class AbstractWorker implements Worker { private boolean successfulWorker = true; private Provider<EntityManager> emProvider; private EntityTransaction transaction; - private UnitOfWork unitOfWork; /** * Subclasses must be annotated with @Component or similar annotation in order for autowiring of dependencies to work @@ -43,14 +41,8 @@ public abstract class AbstractWorker implements Worker { this.emProvider = em; } - @Inject - public void setUnitOfWork(UnitOfWork unitOfWork) { - this.unitOfWork = unitOfWork; - } - @Override public void run() { - unitOfWork.begin(); EntityManager em = emProvider.get(); try { transaction = em.getTransaction(); @@ -98,7 +90,6 @@ public abstract class AbstractWorker implements Worker { if (transaction.isActive() && em.isOpen()) { transaction.rollback(); } - unitOfWork.end(); } } diff --git a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/GradingCompletedMilestoneActivatorTest.java b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/GradingCompletedMilestoneActivatorTest.java index b583b29e21..9c766ce817 100644 --- a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/GradingCompletedMilestoneActivatorTest.java +++ b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/GradingCompletedMilestoneActivatorTest.java @@ -58,7 +58,6 @@ public class GradingCompletedMilestoneActivatorTest { @BeforeEach public void setup() { gradingCompletedMilestoneActivator = new GradingCompletedMilestoneActivator(projectService, daisyAPI, eventBus, userService); - gradingCompletedMilestoneActivator.setUnitOfWork(unitOfWork); gradingCompletedMilestoneActivator.setTxManager(Providers.of(entityManager)); gradingCompletedMilestoneActivator.setWorkerDataService(workerDataService); when(workerDataService.save(any(WorkerData.class))).thenReturn(new WorkerData()); diff --git a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectExporterTest.java b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectExporterTest.java index f4165842bb..d2f53aca29 100644 --- a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectExporterTest.java +++ b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectExporterTest.java @@ -63,7 +63,6 @@ public class ProjectExporterTest { projectExporter = new ProjectExporter(projectRepo, exporterFacade, daisyAPI, externalExporter, finalSeminarService, finalThesisService); projectExporter.setTxManager(Providers.of(entityManager)); projectExporter.setWorkerDataService(workerDataService); - projectExporter.setUnitOfWork(unitOfWork); when(workerDataService.save(any(WorkerData.class))).thenReturn(new WorkerData()); when(entityManager.getTransaction()).thenReturn(entityTransaction); diff --git a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectFinalizerTest.java b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectFinalizerTest.java index 576409136a..52b7094897 100644 --- a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectFinalizerTest.java +++ b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectFinalizerTest.java @@ -48,7 +48,6 @@ public class ProjectFinalizerTest { @BeforeEach public void setup() { projectFinalizer = new ProjectFinalizer(projectService, daisyAPI, thesisApprovedHistoryService); - projectFinalizer.setUnitOfWork(unitOfWork); projectFinalizer.setTxManager(Providers.of(entityManager)); projectFinalizer.setWorkerDataService(workerDataService); when(workerDataService.save(any(WorkerData.class))).thenReturn(new WorkerData()); diff --git a/pom.xml b/pom.xml index 4e6554cb35..2a0ddec0e3 100755 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,8 @@ <module>core</module> <module>view</module> <module>daisy-integration</module> + <module>war</module> + <module>api</module> </modules> <properties> diff --git a/view/pom.xml b/view/pom.xml index d9bcb9393a..09d31eda79 100644 --- a/view/pom.xml +++ b/view/pom.xml @@ -46,10 +46,6 @@ <groupId>org.apache.wicket</groupId> <artifactId>wicket-guice</artifactId> </dependency> - <dependency> - <groupId>org.apache.wicket</groupId> - <artifactId>wicket-spring</artifactId> - </dependency> <dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket-extensions</artifactId> @@ -91,31 +87,6 @@ <version>5.3.2</version> </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-web</artifactId> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-orm</artifactId> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-tx</artifactId> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-webmvc</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-web</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-config</artifactId> - </dependency> - <!-- Servlet API, needed for compilation. --> <dependency> <groupId>jakarta.servlet</groupId> @@ -173,6 +144,15 @@ </dependencies> <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <attachClasses>true</attachClasses> + </configuration> + </plugin> + </plugins> <resources> <resource> <filtering>true</filtering> diff --git a/view/src/main/java/ApplicationBootstrap.java b/view/src/main/java/ApplicationBootstrap.java deleted file mode 100644 index 7b4897a09d..0000000000 --- a/view/src/main/java/ApplicationBootstrap.java +++ /dev/null @@ -1,322 +0,0 @@ -import com.google.common.eventbus.EventBus; -import jakarta.inject.Named; -import jakarta.inject.Provider; -import jakarta.inject.Singleton; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRegistration; -import org.apache.wicket.protocol.http.WicketFilter; -import org.apache.wicket.spring.injection.annot.SpringComponentInjector; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.task.SimpleAsyncTaskExecutor; -import org.springframework.orm.jpa.LocalEntityManagerFactoryBean; -import org.springframework.orm.jpa.SharedEntityManagerCreator; -import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.springframework.web.WebApplicationInitializer; -import org.springframework.web.context.ContextLoaderListener; -import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import org.springframework.web.filter.DelegatingFilterProxy; -import org.springframework.web.servlet.DispatcherServlet; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import se.su.dsv.scipro.SciProApplication; -import se.su.dsv.scipro.daisyExternal.http.DaisyAPI; -import se.su.dsv.scipro.daisyExternal.http.DaisyAPIImpl; -import se.su.dsv.scipro.file.FileService; -import se.su.dsv.scipro.finalseminar.AuthorRepository; -import se.su.dsv.scipro.finalseminar.FinalSeminarActiveParticipationRepository; -import se.su.dsv.scipro.finalseminar.FinalSeminarOppositionRepo; -import se.su.dsv.scipro.finalseminar.FinalSeminarRepository; -import se.su.dsv.scipro.finalseminar.FinalSeminarService; -import se.su.dsv.scipro.finalseminar.FinalSeminarServiceImpl; -import se.su.dsv.scipro.finalseminar.FinalSeminarSettingsService; -import se.su.dsv.scipro.finalseminar.FinalSeminarSettingsServiceImpl; -import se.su.dsv.scipro.gdpr.Reporter; -import se.su.dsv.scipro.misc.DaysService; -import se.su.dsv.scipro.misc.DaysServiceImpl; -import se.su.dsv.scipro.nonworkperiod.NonWorkDayPeriodService; -import se.su.dsv.scipro.nonworkperiod.NonWorkDayPeriodServiceImpl; -import se.su.dsv.scipro.notifications.NotificationService; -import se.su.dsv.scipro.notifications.NotificationServiceImpl; -import se.su.dsv.scipro.profiles.CurrentProfile; -import se.su.dsv.scipro.project.ProjectRepo; -import se.su.dsv.scipro.project.ProjectRepoImpl; -import se.su.dsv.scipro.project.ProjectService; -import se.su.dsv.scipro.project.ProjectServiceImpl; -import se.su.dsv.scipro.report.OppositionReportService; -import se.su.dsv.scipro.report.OppositionReportServiceImpl; -import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; -import se.su.dsv.scipro.springdata.serviceimpls.UserProfileServiceImpl; -import se.su.dsv.scipro.springdata.services.UserProfileService; -import se.su.dsv.scipro.system.FooterAddressRepo; -import se.su.dsv.scipro.system.FooterAddressRepoImpl; -import se.su.dsv.scipro.system.FooterLinkRepo; -import se.su.dsv.scipro.system.FooterLinkRepoImpl; -import se.su.dsv.scipro.system.FooterLinkService; -import se.su.dsv.scipro.system.FooterLinkServiceImpl; -import se.su.dsv.scipro.system.Lifecycle; -import se.su.dsv.scipro.system.ResearchArea; -import se.su.dsv.scipro.system.User; -import se.su.dsv.scipro.system.UserImportService; -import se.su.dsv.scipro.system.UserService; -import se.su.dsv.scipro.system.UserServiceImpl; - -import java.net.CookieHandler; -import java.net.CookieManager; -import java.time.Clock; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -public class ApplicationBootstrap implements WebApplicationInitializer { - @Override - public void onStartup(ServletContext servletContext) - throws ServletException - { - // preserve cookies when using Jersey which will use the logged in session cookie from - // Daisy API thus drastically speeding up the batch jobs - CookieHandler.setDefault(new CookieManager()); - - AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext(); - webApplicationContext.setServletContext(servletContext); - webApplicationContext.register(SciProApplication.class); - webApplicationContext.register(CurrentProfile.class); - webApplicationContext.register(Config.class); - webApplicationContext.refresh(); - - servletContext.addListener(new ContextLoaderListener(webApplicationContext)); - - SciProApplication sciProApplication = webApplicationContext.getBean(SciProApplication.class); - sciProApplication.getComponentInstantiationListeners() - .add(new SpringComponentInjector(sciProApplication, webApplicationContext)); - - servletContext.addFilter("osiv", OpenEntityManagerInViewFilter.class) - .addMappingForUrlPatterns(null, false, "/*"); - - WicketFilter filter = new WicketFilter(sciProApplication); - filter.setFilterPath(""); - - servletContext.addFilter("wicket-filter", filter) - .addMappingForUrlPatterns(null, true, "/*"); - - AnnotationConfigWebApplicationContext dispatcherApplicationContext = new AnnotationConfigWebApplicationContext(); - dispatcherApplicationContext.register(WebConfig.class); - dispatcherApplicationContext.setParent(webApplicationContext); - dispatcherApplicationContext.setServletContext(servletContext); - - ServletRegistration.Dynamic dispatcher = servletContext.addServlet( - "spring-web", - new DispatcherServlet(dispatcherApplicationContext)); - dispatcher.setLoadOnStartup(1); - dispatcher.addMapping("/api/*"); - - DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy("springSecurityFilterChain", dispatcherApplicationContext); - servletContext.addFilter("spring-security-filter", springSecurityFilterChain) - .addMappingForServletNames(null, false, "spring-web"); - } - - @Configuration - @EnableWebMvc - @EnableWebSecurity - @ComponentScan("se.su.dsv.scipro.api") - public static class WebConfig { - @Bean - public SecurityFilterChain basicAuth(HttpSecurity http) - throws Exception - { - return http - .httpBasic(Customizer.withDefaults()) - .authorizeHttpRequests(authorize -> authorize - .anyRequest().authenticated()) - .build(); - } - - @Bean - public UserDetailsService userDetailsService() { - UserDetails userDetails = org.springframework.security.core.userdetails.User.withDefaultPasswordEncoder() - .username("user") - .password("password") - .roles("USER") - .build(); - - return new InMemoryUserDetailsManager(userDetails); - } - } - - @Configuration - @EnableTransactionManagement - public static class Config { - @Bean - @Named("profile") - public String test() { - return "DEV"; - } - - @Bean - public Lifecycle dummy() { - return new Lifecycle() { - @Override - public void start() { - - } - - @Override - public void stop() { - - } - }; - } - - @Bean - public Reporter reporter() { - return user -> null; - } - - @Bean - @Singleton - public LocalEntityManagerFactoryBean entityManagerFactory() { - LocalEntityManagerFactoryBean localEntityManagerFactoryBean = new LocalEntityManagerFactoryBean(); - localEntityManagerFactoryBean.setPersistenceUnitName("defaultPersistenceUnit"); - localEntityManagerFactoryBean.setBootstrapExecutor(new SimpleAsyncTaskExecutor()); - return localEntityManagerFactoryBean; - } - - @Bean - public EntityManager entityManager(EntityManagerFactory entityManagerFactory) { - return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory); - } - - @Bean - public UserService userService(Provider<EntityManager> entityManagerProvider) { - return new UserServiceImpl(entityManagerProvider); - } - - @Bean - public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { - return new org.springframework.orm.jpa.JpaTransactionManager(entityManagerFactory); - } - - @Bean - public UserImportService importService() { - return new UserImportService() { - @Override - public Optional<User> importUser(String userName) { - return Optional.empty(); - } - - @Override - public List<ImportableUser> search(String searchTerm) { - return List.of(); - } - - @Override - public void importUser(ImportableUser importableUser) { - } - - @Override - public Set<ResearchArea> importResearchAreasForSupervisor(User supervisor) { - return Set.of(); - } - }; - } - - @Bean - public UserProfileService userProfileService(Provider<EntityManager> em) { - return new UserProfileServiceImpl(em); - } - - @Bean - public NotificationService notificationService(Provider<EntityManager> em) { - return new NotificationServiceImpl(em); - } - - @Bean - public FooterLinkService footerLinkService(FooterLinkRepo repository) { - return new FooterLinkServiceImpl(repository); - } - - @Bean - public FooterLinkRepo footerLinkRepo(Provider<EntityManager> em) { - return new FooterLinkRepoImpl(em); - } - - @Bean - public FooterAddressRepo footerAddressRepo(Provider<EntityManager> em) { - return new FooterAddressRepoImpl(em); - } - - @Bean - public ProjectService projectService(Provider<EntityManager> em, ProjectRepo projectRepo, Clock clock, - EventBus eventBus) { - return new ProjectServiceImpl(projectRepo, clock, eventBus, em); - } - - @Bean - public ProjectRepo projectRepo(Provider<EntityManager> em) { - return new ProjectRepoImpl(em); - } - - @Bean - public Clock clock() { - return Clock.systemDefaultZone(); - } - - @Bean - public EventBus eventBus() { - return new EventBus(); - } - - @Bean - public FinalSeminarService finalSeminarService(Provider<EntityManager> em, EventBus eventBus, Clock clock) { - AuthorRepository authorRepository = null; - FileService fileService = null; - FinalSeminarOppositionRepo finalSeminarOppositionRepository = null; - FinalSeminarActiveParticipationRepository finalSeminarActiveParticipantRepository = null; - FinalSeminarRepository finalSeminarRepository = null; - RoughDraftApprovalService roughDraftApprovalService = null; - return new FinalSeminarServiceImpl(em, eventBus, authorRepository, fileService, finalSeminarOppositionRepository, finalSeminarActiveParticipantRepository, finalSeminarRepository, clock, roughDraftApprovalService); - } - - @Bean - public FinalSeminarSettingsService finalSeminarSettingsService(Provider<EntityManager> em) { - return new FinalSeminarSettingsServiceImpl(em); - } - - @Bean - public NonWorkDayPeriodService nonWorkDayPeriodService(Provider<EntityManager> em) { - return new NonWorkDayPeriodServiceImpl(em); - } - - @Bean - public DaysService daysService(NonWorkDayPeriodService nonWorkDays) { - return new DaysServiceImpl(nonWorkDays); - } - - @Bean - public OppositionReportService oppositionReportService(Provider<EntityManager> em) { - return new OppositionReportServiceImpl(null, null, null, null); - } - - @Bean - public DaisyAPI daisyAPI( - @Value("${daisy.api.url}") String baseUrl, - @Value("${daisy.api.username}") String username, - @Value("${daisy.api.password}") String password) - { - return new DaisyAPIImpl(baseUrl, username, password); - } - } -} diff --git a/view/src/main/java/DatabaseMigration.java b/view/src/main/java/DatabaseMigration.java deleted file mode 100644 index 442e3003e4..0000000000 --- a/view/src/main/java/DatabaseMigration.java +++ /dev/null @@ -1,35 +0,0 @@ -import org.flywaydb.core.Flyway; - -import javax.naming.InitialContext; -import javax.naming.NamingException; -import jakarta.servlet.ServletContextEvent; -import jakarta.servlet.ServletContextListener; -import javax.sql.DataSource; - -public class DatabaseMigration implements ServletContextListener { - @Override - public void contextInitialized(ServletContextEvent sce) { - migrateDatabase(); - } - - private void migrateDatabase() { - Flyway flyway = Flyway.configure() - .dataSource(getDataSource()) - .baselineOnMigrate(true) - .table("schema_version") - .load(); - flyway.migrate(); - } - - private DataSource getDataSource() { - try { - return InitialContext.doLookup("java:comp/env/jdbc/sciproDS"); - } catch (NamingException e) { - throw new RuntimeException(e); - } - } - - @Override - public void contextDestroyed(ServletContextEvent sce) { - } -} diff --git a/view/src/main/java/se/su/dsv/scipro/FileSystemStore.java b/view/src/main/java/se/su/dsv/scipro/FileSystemStore.java index 7e80019776..3d0eafc255 100644 --- a/view/src/main/java/se/su/dsv/scipro/FileSystemStore.java +++ b/view/src/main/java/se/su/dsv/scipro/FileSystemStore.java @@ -12,7 +12,7 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; -class FileSystemStore implements FileStore { +public class FileSystemStore implements FileStore { private static final int FILES_PER_SUBDIRECTORY = 1000; private static final String FILE_ROOT = "/scipro-files"; diff --git a/war/pom.xml b/war/pom.xml new file mode 100644 index 0000000000..9e5e84b4a6 --- /dev/null +++ b/war/pom.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>SciPro</artifactId> + <version>0.1-SNAPSHOT</version> + </parent> + + <artifactId>war</artifactId> + <packaging>war</packaging> + + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + <exclusions> + <!-- exclude default logging implementation (logback) since we want log4j2 --> + <exclusion> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-logging</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-log4j2</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-tomcat</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-orm</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.wicket</groupId> + <artifactId>wicket-spring</artifactId> + </dependency> + + <!-- <dependency>--> + <!-- <groupId>org.springframework</groupId>--> + <!-- <artifactId>spring-web</artifactId>--> + <!-- </dependency>--> + <!-- <dependency>--> + <!-- <groupId>org.springframework</groupId>--> + <!-- <artifactId>spring-tx</artifactId>--> + <!-- </dependency>--> + <!-- <dependency>--> + <!-- <groupId>org.springframework</groupId>--> + <!-- <artifactId>spring-webmvc</artifactId>--> + <!-- </dependency>--> + <!-- <dependency>--> + <!-- <groupId>org.springframework.security</groupId>--> + <!-- <artifactId>spring-security-web</artifactId>--> + <!-- </dependency>--> + <!-- <dependency>--> + <!-- <groupId>org.springframework.security</groupId>--> + <!-- <artifactId>spring-security-config</artifactId>--> + <!-- </dependency>--> + + <dependency> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>view</artifactId> + <version>${project.version}</version> + <type>war</type> + </dependency> + <dependency> + <!-- needed only at compile time --> + <!-- the classes will be moved as part of the overlay by the war plugin --> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>view</artifactId> + <version>${project.version}</version> + <classifier>classes</classifier> + <optional>true</optional> + </dependency> + <dependency> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>api</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <version>3.4.0</version> + <configuration> + <overlays> + <overlay> + <groupId>se.su.dsv.scipro</groupId> + <artifactId>view</artifactId> + <type>war</type> + </overlay> + </overlays> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/war/src/main/java/se/su/dsv/scipro/war/ApiConfig.java b/war/src/main/java/se/su/dsv/scipro/war/ApiConfig.java new file mode 100644 index 0000000000..efef48e1c5 --- /dev/null +++ b/war/src/main/java/se/su/dsv/scipro/war/ApiConfig.java @@ -0,0 +1,32 @@ +package se.su.dsv.scipro.war; + +import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; +import org.springframework.web.servlet.handler.HandlerMappingIntrospector; + +@Configuration +@ComponentScan("se.su.dsv.scipro.api") +public class ApiConfig { + @Bean + public SecurityFilterChain apiSecurity( + HttpSecurity http, + HandlerMappingIntrospector introspector, + WebMvcProperties webMvcProperties) + throws Exception + { + MvcRequestMatcher.Builder mvc = new MvcRequestMatcher.Builder(introspector) + .servletPath(webMvcProperties.getServlet().getPath()); + return http + .securityMatcher(mvc.pattern("/**")) + .authorizeHttpRequests(authorize -> authorize + .anyRequest().authenticated()) + .httpBasic(Customizer.withDefaults()) + .build(); + } +} diff --git a/war/src/main/java/se/su/dsv/scipro/war/CoreConfig.java b/war/src/main/java/se/su/dsv/scipro/war/CoreConfig.java new file mode 100644 index 0000000000..360419ac35 --- /dev/null +++ b/war/src/main/java/se/su/dsv/scipro/war/CoreConfig.java @@ -0,0 +1,98 @@ +package se.su.dsv.scipro.war; + +import com.google.common.eventbus.EventBus; +import jakarta.inject.Provider; +import jakarta.persistence.EntityManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import se.su.dsv.scipro.FileSystemStore; +import se.su.dsv.scipro.daisyExternal.http.DaisyAPIImpl; +import se.su.dsv.scipro.file.FileStore; +import se.su.dsv.scipro.forum.AbstractThreadRepository; +import se.su.dsv.scipro.forum.AbstractThreadRepositoryImpl; +import se.su.dsv.scipro.grading.GradingHistory; +import se.su.dsv.scipro.grading.GradingHistoryEventRepository; +import se.su.dsv.scipro.grading.GradingServiceImpl; +import se.su.dsv.scipro.oauth.OAuthSettings; +import se.su.dsv.scipro.sukat.LDAP; +import se.su.dsv.scipro.sukat.Sukat; + +import java.time.Clock; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@Configuration +@ComponentScan( + basePackages = "se.su.dsv.scipro", + includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Impl$"), + excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = { + ".*Abstract.*", + ".*[Dd]aisy.*", + ".*GradingServiceImpl" + })) +public class CoreConfig { + private static final int NUMBER_OF_WORKER_THREADS = 4; + + @Bean + public ScheduledExecutorService scheduledExecutorService() { + return Executors.newScheduledThreadPool(NUMBER_OF_WORKER_THREADS); + } + + @Bean + public EventBus eventBus() { + return new EventBus(); + } + + @Bean + public FileStore fileStore() { + return new FileSystemStore(); + } + + @Bean + public Clock clock() { + return Clock.systemDefaultZone(); + } + + @Bean + public GradingHistory gradingHistory(GradingHistoryEventRepository gradingHistoryEventRepository) { + return new GradingHistory(gradingHistoryEventRepository); + } + + @Bean + public OAuthSettings oAuthSettings( + @Value("${oauth.uri}") String uri, + @Value("${oauth.redirectUri}") String redirectUri, + @Value("${oauth.clientId}") String clientId, + @Value("${oauth.clientSecret}") String clientSecret) + { + return new OAuthSettings(uri, redirectUri, clientId, clientSecret); + } + + @Bean + // weird name, both abstract and impl + public AbstractThreadRepository abstractThreadRepository(Provider<EntityManager> em) { + return new AbstractThreadRepositoryImpl(em); + } + + @Bean + public Sukat sukat() { + return new LDAP(); + } + + @Bean + public DaisyAPIImpl daisyAPI( + @Value("${daisy.api.url}") final String baseUrl, + @Value("${daisy.api.username}") final String user, + @Value("${daisy.api.password}") final String password) + { + return new DaisyAPIImpl(baseUrl, user, password); + } + + @Bean + public GradingServiceImpl gradingService(@Value("${service.grading.url}") final String url) { + return new GradingServiceImpl(url); + } +} diff --git a/war/src/main/java/se/su/dsv/scipro/war/Main.java b/war/src/main/java/se/su/dsv/scipro/war/Main.java new file mode 100644 index 0000000000..71a5c80da3 --- /dev/null +++ b/war/src/main/java/se/su/dsv/scipro/war/Main.java @@ -0,0 +1,129 @@ +package se.su.dsv.scipro.war; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import org.apache.wicket.protocol.http.WebApplication; +import org.apache.wicket.protocol.http.WicketFilter; +import org.apache.wicket.spring.injection.annot.SpringComponentInjector; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.springframework.orm.jpa.SharedEntityManagerCreator; +import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter; +import se.su.dsv.scipro.SciProApplication; +import se.su.dsv.scipro.profiles.CurrentProfile; +import se.su.dsv.scipro.system.AggregateUserSearch; +import se.su.dsv.scipro.system.ResearchArea; +import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.system.UserImportService; +import se.su.dsv.scipro.system.UserSearchProvider; +import se.su.dsv.scipro.system.UserSearchService; +import se.su.dsv.scipro.system.UserService; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +@SpringBootApplication +@EntityScan("se.su.dsv.scipro") +@Import({CoreConfig.class, ApiConfig.class}) +public class Main extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { + return builder.sources(Main.class); + } + + @Bean + public EntityManagerFactoryBuilderCustomizer entityManagerFactoryCustomizer() { + return factoryBuilder -> { + factoryBuilder.setBootstrapExecutor(new SimpleAsyncTaskExecutor()); + }; + } + + @Bean + public OpenEntityManagerInViewFilter openEntityManagerInViewFilter() { + return new OpenEntityManagerInViewFilter(); + } + + @Bean + public FilterRegistrationBean<WicketFilter> wicket( + WebApplication webApplication, + ApplicationContext applicationContext) + { + webApplication.getComponentInstantiationListeners() + .add(new SpringComponentInjector(webApplication, applicationContext)); + + WicketFilter filter = new WicketFilter(webApplication); + filter.setFilterPath(""); + + FilterRegistrationBean<WicketFilter> registration = new FilterRegistrationBean<>(); + registration.setFilter(filter); + registration.addUrlPatterns("/*"); + registration.setMatchAfter(true); + return registration; + } + + @Bean + public WebApplication webApplication(CurrentProfile currentProfile) { + return new SciProApplication(currentProfile); + } + + @Bean + public CurrentProfile currentProfile(@Value("${profile}" ) String profile) { + CurrentProfile currentProfile = new CurrentProfile(); + currentProfile.setCurrentProfileString(profile); + return currentProfile; + } + + /** + * Allow injecting of {@link EntityManager} directly instead of {@link EntityManagerFactory} + */ + @Bean + public EntityManager entityManager(EntityManagerFactory emf) { + return SharedEntityManagerCreator.createSharedEntityManager(emf); + } + + /** + * Exists because Spring refuses to inject a collection of beans if there are no beans of that type. + */ + @Bean + public UserImportService dummyUserImportService() { + return new UserImportService() { + @Override + public Optional<User> importUser(String userName) { + return Optional.empty(); + } + + @Override + public List<ImportableUser> search(String searchTerm) { + return List.of(); + } + + @Override + public void importUser(ImportableUser importableUser) { + // do nothing + } + + @Override + public Set<ResearchArea> importResearchAreasForSupervisor(User supervisor) { + return Set.of(); + } + }; + } + + @Bean + public UserSearchService aggregateSearchService( + Set<UserSearchProvider> userSearchProviders, + UserService userService) + { + return new AggregateUserSearch(userSearchProviders, userService); + } +} diff --git a/war/src/main/resources/application.properties b/war/src/main/resources/application.properties new file mode 100644 index 0000000000..24eac8b08f --- /dev/null +++ b/war/src/main/resources/application.properties @@ -0,0 +1,12 @@ +spring.datasource.jndi-name=java:/comp/env/jdbc/sciproDS +spring.flyway.table=schema_version + +# Spring Boot changes Hibernates default naming strategy to convert camelCase to snake_case +# We currently rely on the names to be treated as they are in Java, so we need to set it back to the default +spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + +# We also need to set the implicit strategy to be JPA compliant, as we rely on this naming strategy for certain +# join tables (idea_Keyword vs idea_keyword) +spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl + +spring.mvc.servlet.path=/api diff --git a/view/src/main/webapp/WEB-INF/web.xml b/war/src/main/webapp/WEB-INF/web.xml similarity index 76% rename from view/src/main/webapp/WEB-INF/web.xml rename to war/src/main/webapp/WEB-INF/web.xml index c74f86a197..d78229ac6f 100755 --- a/view/src/main/webapp/WEB-INF/web.xml +++ b/war/src/main/webapp/WEB-INF/web.xml @@ -6,11 +6,7 @@ <display-name>SciPro</display-name> <listener> - <listener-class>ApplicationBootstrap</listener-class> - </listener> - - <listener> - <listener-class>DatabaseMigration</listener-class> + <listener-class>org.springframework.web.SpringServletContainerInitializer</listener-class> </listener> <session-config>