Enable creating an API using Spring Web #5

Merged
niat8586 merged 39 commits from spring into develop 2024-11-06 11:23:29 +01:00
20 changed files with 429 additions and 410 deletions
Showing only changes of commit d25539ca4b - Show all commits

2
.gitignore vendored
View File

@ -23,3 +23,5 @@ view/target
*.log
fitnesse/target/
daisy-integration/target/
war/target/
api/target/

25
api/pom.xml Normal file
View File

@ -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>

View File

@ -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) {
}

View File

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

View File

@ -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();
}
}

View File

@ -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());

View File

@ -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);

View File

@ -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());

View File

@ -13,6 +13,8 @@
<module>core</module>
<module>view</module>
<module>daisy-integration</module>
<module>war</module>
<module>api</module>
</modules>
<properties>

View File

@ -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>

View File

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

View File

@ -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) {
}
}

View File

@ -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";

116
war/pom.xml Normal file
View File

@ -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>

View File

@ -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();
}
}

View File

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

View File

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

View File

@ -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

View File

@ -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>