Fit for tomcat deployment

This commit is contained in:
Patrick Haßel 2022-03-23 11:30:46 +01:00
parent 172ee3a337
commit 536218920a
20 changed files with 82 additions and 182 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@
/target/ /target/
/.idea/ /.idea/
/.jpb/ /.jpb/
/application.properties /*.db

10
application.properties Normal file
View File

@ -0,0 +1,10 @@
#logging.level.de.ph87.homeautomation=DEBUG
#-
spring.datasource.url=jdbc:h2:./Homeautomation;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
#-
spring.jpa.hibernate.ddl-auto=update
de.ph87.homeautomation.insert-demo-data=true

View File

@ -1,7 +0,0 @@
#!/bin/bash
cd "$(dirname "$0")" || exit 1
mvn clean package spring-boot:repackage && \
scp target/Homeautomation.jar media@10.0.0.50:/home/media/java/Homeautomation/Homeautomation.jar.update && \
curl -m 2 -s http://10.0.0.50:8080/server/shutdown && echo "Server restarting..." || echo "Failed to restart server!"

34
pom.xml
View File

@ -7,16 +7,17 @@
<groupId>de.ph87</groupId> <groupId>de.ph87</groupId>
<artifactId>Homeautomation</artifactId> <artifactId>Homeautomation</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties> <properties>
<maven.compiler.source>15</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target> <maven.compiler.target>11</maven.compiler.target>
</properties> </properties>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version> <version>2.6.4</version>
</parent> </parent>
<dependencies> <dependencies>
@ -28,10 +29,6 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>
@ -40,15 +37,24 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId> <artifactId>spring-boot-starter-websocket</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency> </dependency>
<dependency> <dependency>
@ -148,18 +154,6 @@
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<configuration>
<mainClass>de.ph87.homeautomation.BackendApplication</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -1,5 +1,8 @@
# See http://help.github.com/ignore-files/ for more about ignoring files. # See http://help.github.com/ignore-files/ for more about ignoring files.
/node/
### PATRICK
/node
### END
# compiled output # compiled output
/dist /dist

View File

@ -4,7 +4,7 @@
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"build": "ng build", "build": "ng build --base-href /Homeautomation/",
"watch": "ng build --watch --configuration development", "watch": "ng build --watch --configuration development",
"test": "ng test" "test": "ng test"
}, },

View File

@ -5,6 +5,7 @@ import {environment} from "../../environments/environment";
import {Subject} from "rxjs"; import {Subject} from "rxjs";
import {CompatClient, Stomp} from "@stomp/stompjs"; import {CompatClient, Stomp} from "@stomp/stompjs";
import {Update} from "./Update"; import {Update} from "./Update";
import {LocationStrategy} from "@angular/common";
export function NO_OP() { export function NO_OP() {
} }
@ -21,7 +22,7 @@ function errorInterceptor(errorHandler: (error: any) => void): ((error: any) =>
} }
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
export class ApiService { export class ApiService {
@ -30,10 +31,12 @@ export class ApiService {
private updateSubject = new Subject<Update<object>>(); private updateSubject = new Subject<Update<object>>();
constructor( constructor(
private http: HttpClient, protected readonly http: HttpClient,
protected readonly locationStrategy: LocationStrategy,
) { ) {
const url = this.websocketUrl("websocket");
this.webSocketClient = Stomp.over(function () { this.webSocketClient = Stomp.over(function () {
return new WebSocket(ApiService.url("ws", "websocket")); return new WebSocket(url);
}); });
this.webSocketClient.debug = () => null; this.webSocketClient.debug = () => null;
this.webSocketClient.connect({}, () => { this.webSocketClient.connect({}, () => {
@ -46,27 +49,31 @@ export class ApiService {
} }
getItem<T>(path: string, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) { getItem<T>(path: string, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) {
this.http.get<any>(ApiService.url("http", path)).pipe(map(fromJson)).subscribe(next, errorInterceptor(error)); this.http.get<any>(this.restUrl(path)).pipe(map(fromJson)).subscribe(next, errorInterceptor(error));
} }
getList<T>(path: string, fromJson: (json: any) => T, compare: (a: T, b: T) => number = NO_COMPARE, next: (list: T[]) => void = NO_OP, error: (error: any) => void = NO_OP) { getList<T>(path: string, fromJson: (json: any) => T, compare: (a: T, b: T) => number = NO_COMPARE, next: (list: T[]) => void = NO_OP, error: (error: any) => void = NO_OP) {
this.http.get<any[]>(ApiService.url("http", path)).pipe(map(list => list.map(fromJson).sort(compare))).subscribe(next, errorInterceptor(error)); this.http.get<any[]>(this.restUrl(path)).pipe(map(list => list.map(fromJson).sort(compare))).subscribe(next, errorInterceptor(error));
} }
postReturnNone<T>(path: string, data: any, next: (_: void) => void = NO_OP, error: (error: any) => void = NO_OP) { postReturnNone<T>(path: string, data: any, next: (_: void) => void = NO_OP, error: (error: any) => void = NO_OP) {
this.http.post<any>(ApiService.url("http", path), data).subscribe(next, errorInterceptor(error)); this.http.post<any>(this.restUrl(path), data).subscribe(next, errorInterceptor(error));
} }
postReturnItem<T>(path: string, data: any, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) { postReturnItem<T>(path: string, data: any, fromJson: (json: any) => T, next: (item: T) => void = NO_OP, error: (error: any) => void = NO_OP) {
this.http.post<any>(ApiService.url("http", path), data).pipe(map(fromJson)).subscribe(next, errorInterceptor(error)); this.http.post<any>(this.restUrl(path), data).pipe(map(fromJson)).subscribe(next, errorInterceptor(error));
} }
postReturnList<T>(path: string, data: any, fromJson: (json: any) => T, next: (list: T[]) => void = NO_OP, error: (error: any) => void = NO_OP) { postReturnList<T>(path: string, data: any, fromJson: (json: any) => T, next: (list: T[]) => void = NO_OP, error: (error: any) => void = NO_OP) {
this.http.post<any>(ApiService.url("http", path), data).pipe(map(list => list.map(fromJson))).subscribe(next, errorInterceptor(error)); this.http.post<any>(this.restUrl(path), data).pipe(map(list => list.map(fromJson))).subscribe(next, errorInterceptor(error));
} }
private static url(schema: string, path: string): string { private restUrl(path: string): string {
return schema + "://" + environment.host + ":" + environment.port + "/" + path; return environment.restBase + this.locationStrategy.getBaseHref() + path;
}
private websocketUrl(path: string) {
return environment.websocketBase + this.locationStrategy.getBaseHref() + path;
} }
} }

View File

@ -3,8 +3,8 @@ import {ComponentFixture, TestBed} from '@angular/core/testing';
import {SearchComponent} from './search.component'; import {SearchComponent} from './search.component';
describe('SearchComponent', () => { describe('SearchComponent', () => {
let component: SearchComponent; let component: SearchComponent<any>;
let fixture: ComponentFixture<SearchComponent>; let fixture: ComponentFixture<SearchComponent<any>>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({

View File

@ -0,0 +1,6 @@
const secure: boolean = window.location.protocol === "https:";
const host: string = window.location.host.split(":", 1)[0];
export function getBaseUrl(protocol: string, port: number) {
return protocol + (secure ? 's' : '') + "://" + host + ":" + port;
}

View File

@ -1,5 +1,7 @@
import {getBaseUrl} from "./UrlHelper";
export const environment = { export const environment = {
production: true, production: true,
host: window.location.host.split(":")[0], restBase: '',
port: window.location.port, websocketBase: getBaseUrl('ws', parseInt(window.location.port)),
}; };

View File

@ -2,10 +2,12 @@
// `ng build` replaces `environment.ts` with `environment.prod.ts`. // `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`. // The list of file replacements can be found in `angular.json`.
import {getBaseUrl} from "./UrlHelper";
export const environment = { export const environment = {
production: false, production: false,
host: window.location.host.split(":")[0], restBase: getBaseUrl('http', 8080),
port: 8080, websocketBase: getBaseUrl('ws', 8080),
}; };
/* /*

View File

@ -1,28 +1,14 @@
package de.ph87.homeautomation; package de.ph87.homeautomation;
import de.ph87.homeautomation.knx.group.KnxGroupImportService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import javax.annotation.PostConstruct;
@SpringBootApplication @SpringBootApplication
@RequiredArgsConstructor public class BackendApplication extends SpringBootServletInitializer {
public class BackendApplication {
private final DemoDataService demoDataService;
private final KnxGroupImportService knxGroupImportService;
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(BackendApplication.class); SpringApplication.run(BackendApplication.class);
} }
@PostConstruct
public void postConstruct() {
knxGroupImportService.importGroups();
demoDataService.insertDemoData();
}
} }

View File

@ -2,7 +2,6 @@ package de.ph87.homeautomation;
import com.luckycatlabs.sunrisesunset.Zenith; import com.luckycatlabs.sunrisesunset.Zenith;
import de.ph87.homeautomation.channel.Channel; import de.ph87.homeautomation.channel.Channel;
import de.ph87.homeautomation.device.DeviceRepository;
import de.ph87.homeautomation.device.DeviceWriteService; import de.ph87.homeautomation.device.DeviceWriteService;
import de.ph87.homeautomation.device.devices.DeviceDto; import de.ph87.homeautomation.device.devices.DeviceDto;
import de.ph87.homeautomation.knx.group.KnxGroup; import de.ph87.homeautomation.knx.group.KnxGroup;
@ -10,12 +9,10 @@ import de.ph87.homeautomation.knx.group.KnxGroupReadService;
import de.ph87.homeautomation.logic.Logic; import de.ph87.homeautomation.logic.Logic;
import de.ph87.homeautomation.logic.LogicOperator; import de.ph87.homeautomation.logic.LogicOperator;
import de.ph87.homeautomation.logic.LogicRepository; import de.ph87.homeautomation.logic.LogicRepository;
import de.ph87.homeautomation.logic.LogicWriter;
import de.ph87.homeautomation.property.Property; import de.ph87.homeautomation.property.Property;
import de.ph87.homeautomation.property.PropertyRepository; import de.ph87.homeautomation.property.PropertyRepository;
import de.ph87.homeautomation.property.PropertyType; import de.ph87.homeautomation.property.PropertyType;
import de.ph87.homeautomation.scene.SceneDto; import de.ph87.homeautomation.scene.SceneDto;
import de.ph87.homeautomation.scene.SceneRepository;
import de.ph87.homeautomation.scene.SceneWriteService; import de.ph87.homeautomation.scene.SceneWriteService;
import de.ph87.homeautomation.schedule.Schedule; import de.ph87.homeautomation.schedule.Schedule;
import de.ph87.homeautomation.schedule.ScheduleRepository; import de.ph87.homeautomation.schedule.ScheduleRepository;
@ -45,17 +42,13 @@ public class DemoDataService {
private final DeviceWriteService deviceWriteService; private final DeviceWriteService deviceWriteService;
private final DeviceRepository deviceRepository;
private final PropertyRepository propertyRepository; private final PropertyRepository propertyRepository;
private final KnxGroupReadService knxGroupReadService; private final KnxGroupReadService knxGroupReadService;
private final SceneRepository sceneRepository;
private final SceneWriteService sceneWriteService; private final SceneWriteService sceneWriteService;
private final LogicWriter logicWriter;
private final LogicRepository logicRepository; private final LogicRepository logicRepository;
@ -172,7 +165,7 @@ public class DemoDataService {
} }
private KnxGroup knx(final int main, final int mid, final int sub) { private KnxGroup knx(final int main, final int mid, final int sub) {
return knxGroupReadService.getByAddress(main, mid, sub); return knxGroupReadService.getByAddress(main, mid, sub).orElse(null);
} }
private Property createProperty(final String title, final PropertyType type, final Channel readChannel, final Channel writeChannel) { private Property createProperty(final String title, final PropertyType type, final Channel readChannel, final Channel writeChannel) {

View File

@ -1,20 +0,0 @@
package de.ph87.homeautomation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("server")
@RequiredArgsConstructor
public class ServerController {
private final ServerService serverService;
@GetMapping("shutdown")
public void shutdown() {
serverService.shutdown();
}
}

View File

@ -1,43 +0,0 @@
package de.ph87.homeautomation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@EnableAsync
@RequiredArgsConstructor
public class ServerService implements ApplicationContextAware {
private static final int DELAY_SECONDS = 1;
private ConfigurableApplicationContext applicationContext;
@Async
public void shutdown() {
try {
for (int delay = DELAY_SECONDS; delay > 0; delay--) {
log.info("Shutdown in {} second.", delay);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
log.warn("Shutdown interrupted.");
return;
}
applicationContext.close();
System.exit(0);
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.applicationContext = (ConfigurableApplicationContext) ctx;
}
}

View File

@ -1,5 +1,7 @@
package de.ph87.homeautomation.property; package de.ph87.homeautomation;
import de.ph87.homeautomation.knx.group.KnxGroupImportService;
import de.ph87.homeautomation.property.PropertyWriteService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.ApplicationStartedEvent;
@ -9,12 +11,18 @@ import org.springframework.stereotype.Service;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class PropertyEventListener { public class StartupService {
private final KnxGroupImportService knxGroupImportService;
private final DemoDataService demoDataService;
private final PropertyWriteService propertyWriteService; private final PropertyWriteService propertyWriteService;
@EventListener(ApplicationStartedEvent.class) @EventListener(ApplicationStartedEvent.class)
public void onApplicationStarted() { public void startup() {
knxGroupImportService.importGroups();
demoDataService.insertDemoData();
propertyWriteService.updateAllProperties(); propertyWriteService.updateAllProperties();
} }

View File

@ -7,6 +7,7 @@ import org.springframework.transaction.annotation.Transactional;
import tuwien.auto.calimero.GroupAddress; import tuwien.auto.calimero.GroupAddress;
import java.util.List; import java.util.List;
import java.util.Optional;
@Slf4j @Slf4j
@Service @Service
@ -16,8 +17,8 @@ public class KnxGroupReadService {
private final KnxGroupRepository knxGroupRepository; private final KnxGroupRepository knxGroupRepository;
public KnxGroup getByAddress(final int main, final int mid, final int sub) { public Optional<KnxGroup> getByAddress(final int main, final int mid, final int sub) {
return knxGroupRepository.findByAddressRaw(new GroupAddress(main, mid, sub).getRawAddress()).orElseThrow(RuntimeException::new); return knxGroupRepository.findByAddressRaw(new GroupAddress(main, mid, sub).getRawAddress());
} }
public List<KnxGroup> findAll() { public List<KnxGroup> findAll() {

View File

@ -1,18 +1,9 @@
package de.ph87.homeautomation.web; package de.ph87.homeautomation.web;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@ -21,34 +12,8 @@ import org.springframework.web.servlet.resource.PathResourceResolver;
import java.io.IOException; import java.io.IOException;
@Configuration @Configuration
@EnableWebSecurity
@RequiredArgsConstructor @RequiredArgsConstructor
public class WebConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer { public class WebConfig implements WebMvcConfigurer {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
http.csrf().disable();
http.authorizeRequests().anyRequest().permitAll();
}
@Override
public void configure(WebSecurity web) {
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
final StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);
return firewall;
}
@Override @Override
public void addCorsMappings(CorsRegistry registry) { public void addCorsMappings(CorsRegistry registry) {

View File

@ -1,17 +1,10 @@
logging.level.root=WARN logging.level.root=WARN
logging.level.de.ph87=INFO logging.level.de.ph87=INFO
#- #-
spring.datasource.url=jdbc:h2:./Homeautomation;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
#-
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=false spring.jpa.open-in-view=false
#- #-
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
#-
spring.jackson.serialization.indent_output=true spring.jackson.serialization.indent_output=true
#- #-
spring.main.banner-mode=off spring.main.banner-mode=off