Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • arsnova/arsnova-backend
  • pcvl72/arsnova-backend
  • tksl38/arsnova-backend
3 results
Show changes
Commits on Source (732)
Showing
with 2003 additions and 711 deletions
variables:
MIRROR_REPO: git@github.com:thm-projects/arsnova-backend.git
WAR_FILE: target/arsnova-backend-*.war
OUTPUT_DIR: target
WAR_FILE: $OUTPUT_DIR/arsnova-backend-*.war
stages:
- analysis
- test
- build
- post-build
- deploy
- synchronization
cache:
paths:
- .m2/
.maven: &maven
image: maven:3-jdk-11-slim
variables:
MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss.SSS -Djava.awt.headless=true"
MAVEN_CLI_OPTS: "--batch-mode --fail-at-end --show-version"
cache:
key: maven-repository
paths:
- .m2/repository
sync_mirror:
stage: synchronization
when: always
except:
- production
- staging
tags:
- git
allow_failure: true
checkstyle:
<<: *maven
stage: build
script:
- mkdir ~/.ssh && echo "$GITHUB_HOST_KEY" > ~/.ssh/known_hosts
- eval $(ssh-agent -s) && ssh-add <(echo "$GITHUB_DEPLOY_KEY")
- git clone --bare "$CI_REPOSITORY_URL" mirror.git
- cd mirror.git
- git update-ref -d refs/tags/production
- git update-ref -d refs/tags/staging
- git push --mirror "$MIRROR_REPO"
- mvn $MAVEN_CLI_OPTS checkstyle:check -Dcheckstyle.missing-javadoc.severity=info
sonar:
stage: analysis
only:
- master
tags:
- maven
checkstyle_javadoc:
<<: *maven
stage: build
allow_failure: true
script:
- mvn sonar:sonar
- mvn $MAVEN_CLI_OPTS checkstyle:check -Dcheckstyle.missing-javadoc.severity=warning
checkstyle:
stage: analysis
tags:
- maven
compile:
<<: *maven
stage: build
artifacts:
paths:
- $OUTPUT_DIR
script:
- mvn checkstyle:checkstyle
- mvn $MAVEN_CLI_OPTS test-compile
test:
stage: test
tags:
- maven
unit_test:
<<: *maven
stage: post-build
dependencies:
- compile
artifacts:
paths:
- $OUTPUT_DIR
coverage: '/Code coverage: \d+\.\d+/'
script:
- mvn test
- mvn $MAVEN_CLI_OPTS jacoco:prepare-agent surefire:test jacoco:report
- awk -F"," '{ instructions += $4 + $5; covered += $5 } END { print "Instructions covered:", covered, "/", instructions; print "Code coverage:", 100 * covered / instructions "%" }' "$OUTPUT_DIR/site/jacoco/jacoco.csv"
package:
stage: build
tags:
- maven
<<: *maven
stage: post-build
dependencies:
- compile
artifacts:
name: package
paths:
- $WAR_FILE
script:
- mvn package -DskipTests
- mvn $MAVEN_CLI_OPTS war:war
tomcat_production:
.deploy: &deploy
stage: deploy
only:
- production
image: python:3
when: manual
variables:
DEPLOY_CONTEXT: api
GIT_STRATEGY: none
dependencies:
- package
script:
- curl --fail --upload-file $WAR_FILE "https://$PROD_TOMCAT_USER:$PROD_TOMCAT_PASSWORD@$PROD_TOMCAT_HOST/manager/text/deploy?path=%2Fapi&update=true"
# Do some variable magic to access host-specific variables
- PREFIX=$(echo $CI_ENVIRONMENT_NAME | tr '.:/-' '_')
- HOST_VAR=${PREFIX}__HOST TOMCAT_PASSWORD_VAR=${PREFIX}__TOMCAT_PASSWORD TOMCAT_USER_VAR=${PREFIX}__TOMCAT_USER
- "[ -z \"${!HOST_VAR}\" ] && echo \"No configuration for $DEPLOY_HOST found.\" && exit 1"
# Abort if there are too many users online
- USER_COUNT=$(curl -fsSL ${CI_ENVIRONMENT_URL}statistics | python -c "import sys, json; data=json.loads(sys.stdin.read()); print(data['activeUsers']);")
- "[ \"$USER_COUNT\" -ge 10 ] && [ -z \"$FORCE\" ] && echo \"Too many users ($USER_COUNT) online.\" && exit 1"
# Deploy .war file to Tomcat
- curl -fsS --upload-file $WAR_FILE "https://${!TOMCAT_USER_VAR}:${!TOMCAT_PASSWORD_VAR}@${!HOST_VAR}/manager/text/deploy?path=%2F${DEPLOY_CONTEXT}&update=true"
tomcat_production:
<<: *deploy
environment:
name: production/$PROD_DEPLOY_HOST
url: https://$PROD_DEPLOY_HOST/$DEPLOY_CONTEXT/
only:
variables:
- $PROD_DEPLOY_HOST
# GitLab 11.0+
#- $PROD_DEPLOY_HOST =~ /^([a-z0-9-]+\.)*[a-z0-9-]+(:[0-9]+)?$/
refs:
- /^v[0-9]+/
- /^[0-9]+\.[0-9]+$/
before_script:
- DEPLOY_HOST=$PROD_DEPLOY_HOST
tomcat_development:
<<: *deploy
environment:
name: development/$DEV_DEPLOY_HOST
url: https://$DEV_DEPLOY_HOST/api/
only:
variables:
- $DEV_DEPLOY_HOST
# GitLab 11.0+
#- $DEV_DEPLOY_HOST =~ /^([a-z0-9-]+\.)*[a-z0-9-]+(:[0-9]+)?$/
before_script:
- DEPLOY_HOST=$DEV_DEPLOY_HOST
sonarqube:
<<: *maven
stage: deploy
only:
- staging
- master
dependencies:
- compile
- unit_test
allow_failure: true
script:
- curl --fail --upload-file $WAR_FILE "https://$DEV_TOMCAT_USER:$DEV_TOMCAT_PASSWORD@$DEV_TOMCAT_HOST/manager/text/deploy?path=%2Fapi&update=true"
- mvn $MAVEN_CLI_OPTS sonar:sonar
dist: trusty
dist: xenial
language: java
jdk:
- openjdk8
- oraclejdk8
sudo: false
notifications:
slack: f112a:GOZb6ewuAf2AVxduDXzdiCTG
email: admin@arsnova.eu
- openjdk11
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<!--
Checkstyle configuration that checks the sun coding conventions from:
- the Java Language Specification at
http://java.sun.com/docs/books/jls/second_edition/html/index.html
- the Sun Code Conventions at http://java.sun.com/docs/codeconv/
- the Javadoc guidelines at
http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
- the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
- some best practices
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.sf.net (or in your downloaded distribution).
Most Checks are configurable, be sure to consult the documentation.
To completely disable a check, just comment it out or delete it from the file.
Finally, it is worth reading the documentation.
-->
<module name="Checker">
<!--
If you set the basedir property below, then all reported file
names will be relative to the specified directory. See
http://checkstyle.sourceforge.net/5.x/config.html#Checker
<property name="basedir" value="${basedir}"/>
-->
<!-- Checks that each Java package has a Javadoc file used for commenting. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->
<module name="JavadocPackage">
<property name="allowLegacy" value="true"/>
</module>
<!-- Checks whether files end with a new line. -->
<!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- Checks that property files contain the same keys. -->
<!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
<module name="Translation"/>
<module name="FileLength"/>
<!-- Following interprets the header file as regular expressions. -->
<!-- <module name="RegexpHeader"/> -->
<!--module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module-->
<module name="RegexpSingleline">
<!-- \s matches whitespace character, $ matches end of line. -->
<property name="format" value="\s+$"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<module name="TreeWalker">
<property name="cacheFile" value="${checkstyle.cache.file}"/>
<property name="tabWidth" value="4"/>
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<!-- module name="JavadocMethod">
<property name="allowMissingJavadoc" value="true"/>
</module>
<module name="JavadocType"/>
<module name="JavadocVariable"/>
<module name="JavadocStyle"/-->
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for Headers -->
<!-- See http://checkstyle.sf.net/config_header.html -->
<!-- <module name="Header"> -->
<!-- The follow property value demonstrates the ability -->
<!-- to have access to ANT properties. In this case it uses -->
<!-- the ${basedir} property to allow Checkstyle to be run -->
<!-- from any directory within a project. See property -->
<!-- expansion, -->
<!-- http://checkstyle.sf.net/config.html#properties -->
<!-- <property -->
<!-- name="headerFile" -->
<!-- value="${basedir}/java.header"/> -->
<!-- </module> -->
<!-- Checks for imports -->
<!-- See http://checkstyle.sf.net/config_import.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="LineLength">
<property name="max" value="120" />
</module>
<module name="MethodLength"/>
<module name="ParameterNumber"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="EmptyForIteratorPad"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<!-- Modifier Checks -->
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Checks for blocks. You know, those {}'s -->
<!-- See http://checkstyle.sf.net/config_blocks.html -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly"/>
<!-- Checks for common coding problems -->
<!-- See http://checkstyle.sf.net/config_coding.html -->
<!-- module name="AvoidInlineConditionals" /-->
<!-- module name="DoubleCheckedLocking"/--> <!-- MY FAVOURITE -->
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="HiddenField">
<property name="ignoreSetter" value="true"/>
</module>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<module name="MagicNumber"/>
<module name="MissingSwitchDefault"/>
<!-- module name="RedundantThrows"/ -->
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<!-- Checks for class design -->
<!-- See http://checkstyle.sf.net/config_design.html -->
<!-- module name="DesignForExtension"/ -->
<!-- module name="FinalClass"/ -->
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<module name="VisibilityModifier"/>
<!-- Miscellaneous other checks. -->
<!-- See http://checkstyle.sf.net/config_misc.html -->
<module name="ArrayTypeStyle"/>
<!-- module name="FinalParameters"/ -->
<module name="TodoComment"/>
<module name="UpperEll"/>
</module>
</module>
# Changelog
## 2.7.3
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.7.2
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.7.1
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.6.4
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.5.10
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.7
Features:
* Account deletion: Users can now delete their own accounts. Admins can delete
any user account. Account deletion removes sessions and their contents created
by the user and anonymizes data created through participation in other
sessions.
* Auto-deletion: Accounts can be deleted automatically after a configurable
period of inactivity.
* OpenID Connect: OIDC is now supported for authentication. Configuration
discovery support is required.
Improvements:
* Public Session Pool: Added API endpoint to clone a session from the pool.
Previously, cloning had to be performed by the client.
Bug fixes:
* Import/Export: The handling of session features during import has been fixed.
The raw exported data can now be imported without further manipulation by the
client.
**This version is brought to you by:**
Project management: Klaus Quibeldey-Cirkel
Lead programming: Daniel Gerhardt, Tom "tekay" Käsler
Sponsoring: [AG QLS](https://www.thm.de/site/en/hochschule/service/ag-qls.html),
[HMWK](https://wissenschaft.hessen.de/wissenschaft/it-neue-medien/kompetenznetz-e-learning-hessen)
## 2.6.3
Bug fixes:
* The backend now correctly responds with 4xx error codes instead of 500 to less
common errors caused by bad requests from the client-side.
Additional changes:
* Libraries have been upgraded to fix potential bugs.
## 2.5.9
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.6.2
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.5.8
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.6.1
Bug fixes:
* Fixed exception at startup caused by missing migration document.
* Fixed session export to correctly include all answers.
Additional changes:
* Libraries have been upgraded to fix potential bugs.
## 2.6
Features:
* Experimental support for CouchDB 2 has been added. Note: The data migration
script is not compatible with CouchDB 2 and has to be run before an upgrade.
Improvements:
* Error handling and logging has been improved. It should now be easier to find
the cause of problems. API error responses now contain the name of the
`Exception` which caused the error. Further details for debugging purposes can
be enabled with the new `api.expose-exception-messages` setting (Do NOT
enable in production environments!).
* Updated OAuth handling to restore compatibility with 3rd-party login services.
Bug fixes:
* Fixed multiple bugs caused by incorrect type handling in the database layer.
* Fixed XFO header check behind reverse proxy (used by clients when embedding
external websites).
* Fixed rounding error in learning progress calculation.
* Fixed `security.cors.origins` setting.
* Fixed import of data from older versions.
Security:
* Fixed DoS vulnerability in authentication handling behind reverse proxy.
Configuration changes:
Minor changes to the web server and Tomcat proxy configuration are required
(see [installation guide](src/site/markdown/installation.md)).
**This version is brought to you by:**
Project management: Klaus Quibeldey-Cirkel
Lead programming: Daniel Gerhardt, Tom "tekay" Käsler
Contributions: Marius Renner, Paul-Christian Volkmer
Sponsoring: [AG QLS](https://www.thm.de/site/en/hochschule/service/ag-qls.html),
[HMWK](https://wissenschaft.hessen.de/wissenschaft/it-neue-medien/kompetenznetz-e-learning-hessen)
## 2.5.7
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.5.6
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.5.5
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.5.4
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.5.3
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.5.2
This is a maintenance release which only brings libraries up to date to fix
potential bugs.
## 2.5.1
This release fixes a performance issue on session creation affecting large
installations.
......
# ARSnova
---
The next major version of ARSnova Backend is being developed on the `master` branch which is not yet production ready.
If you are looking for the current stable code base, switch to branch `2.x`.
---
ARSnova is a modern approach to Audience Response Systems (ARS).
It is released under the GPLv3 license, and is offered as a Software as a Service free of charge.
Head over to [arsnova.eu](https://arsnova.eu/) to see it in action.
......
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the coding conventions for ARSnova Java
projects. The conventions and configuration are based on Google coding
conventions from Google Java Style that can be found at
https://google.github.io/styleguide/javaguide.html.
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.sf.net (or in your downloaded distribution).
To completely disable a check, just comment it out or delete it from the file.
Original authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
-->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="warning"/>
<module name="SeverityMatchFilter">
<property name="severity" value="info"/>
<property name="acceptOnMatch" value="false"/>
</module>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Excludes all 'module-info.java' files -->
<!-- See https://checkstyle.org/config_filefilters.html -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<module name="TreeWalker">
<!-- Checks for whitespace -->
<property name="tabWidth" value="2"/>
<module name="RegexpSinglelineJava">
<property name="format" value="^\t* "/>
<property name="message" value="Indent must use tab characters"/>
<property name="ignoreComments" value="true"/>
</module>
<module name="OuterTypeFilename"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="LineLength">
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="AvoidStarImport"/>
<module name="OneTopLevelClass"/>
<module name="NoLineWrap"/>
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="NeedBraces"/>
<module name="LeftCurly"/>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
LITERAL_DO"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens"
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
INSTANCE_INIT"/>
</module>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyLambdas" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<message key="ws.notFollowed"
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded"
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
<module name="OneStatementPerLine"/>
<module name="MultipleVariableDeclarations"/>
<module name="ArrayTypeStyle"/>
<module name="MissingSwitchDefault"/>
<module name="FallThrough"/>
<module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapDot"/>
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapComma"/>
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapMethodRef"/>
<property name="tokens" value="METHOD_REF"/>
<property name="option" value="nl"/>
</module>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern"
value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LambdaParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="CatchParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalVariableName">
<property name="tokens" value="VARIABLE_DEF"/>
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="NoFinalizer"/>
<module name="GenericWhitespace">
<message key="ws.followed"
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded"
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow"
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded"
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="Indentation">
<property name="basicOffset" value="2"/>
<property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="2"/>
<property name="throwsIndent" value="4"/>
<property name="lineWrappingIndentation" value="4"/>
<property name="arrayInitIndent" value="2"/>
</module>
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="1"/>
</module>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS"/>
<property name="specialImportsRegExp" value="^de\.thm\.arsnova"/>
</module>
<module name="MethodParamPad"/>
<module name="NoWhitespaceBefore">
<property name="tokens"
value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
<property name="allowLineBreaks" value="true"/>
</module>
<module name="ParenPad"/>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens"
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<module name="NonEmptyAtclauseDescription"/>
<module name="JavadocTagContinuationIndentation"/>
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments"
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<module name="JavadocParagraph"/>
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module>
<module name="JavadocMethod">
<property name="scope" value="public"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingThrowsTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="allowThrowsTagsForSubclasses" value="true"/>
</module>
<module name="MissingJavadocMethod">
<property name="severity" value="${checkstyle.missing-javadoc.severity}" default="warning"/>
<property name="scope" value="public"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test"/>
</module>
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
<message key="name.invalidPattern"
value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false"/>
</module>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="CommentsIndentation"/>
<!-- ARSnova-specific rules -->
<module name="FinalLocalVariable">
<property name="tokens" value="VARIABLE_DEF,PARAMETER_DEF"/>
<property name="validateEnhancedForLoopVariable" value="true"/>
</module>
<module name="RedundantModifier"/>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
</module>
</module>
This diff is collapsed.
/**
* Classes and interfaces to support aspect-oriented programming
*/
package de.thm.arsnova.aop;
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -15,116 +15,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.cache;
import de.thm.arsnova.events.*;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Component;
package de.thm.arsnova.cache;
/**
* This class is used to evict caches based on events. The events carry all necessary information to clear the
* caches, e.g, for a specific session.
* This interface is used as a tag to make Spring dependency injection happy...
*/
@Component
public class CacheBuster implements ICacheBuster, NovaEventVisitor {
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(NewInterposedQuestionEvent event) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(DeleteInterposedQuestionEvent event) { }
@Override
public void visit(NewQuestionEvent event) { }
@Override
public void visit(UnlockQuestionEvent event) { }
@Override
public void visit(UnlockQuestionsEvent newQuestionsEvent) { }
@Override
public void visit(LockQuestionEvent lockQuestionEvent) { }
@Override
public void visit(LockQuestionsEvent lockQuestionsEvent) { }
@CacheEvict(value = "answers", key = "#event.Question")
@Override
public void visit(NewAnswerEvent event) { }
@Override
public void visit(DeleteAnswerEvent event) { }
@Override
public void visit(DeleteQuestionEvent event) { }
@Override
public void visit(DeleteAllQuestionsEvent event) { }
@Override
public void visit(DeleteAllQuestionsAnswersEvent event) { }
@Override
public void visit(DeleteAllPreparationAnswersEvent event) { }
@Override
public void visit(DeleteAllLectureAnswersEvent event) { }
@Override
public void visit(NewFeedbackEvent event) { }
@Override
public void visit(DeleteFeedbackForSessionsEvent event) { }
@Override
public void visit(StatusSessionEvent event) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(ChangeLearningProgressEvent changeLearningProgress) { }
@Override
public void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent) { }
@Override
public void visit(PiRoundEndEvent piRoundEndEvent) { }
@Override
public void visit(PiRoundCancelEvent piRoundCancelEvent) { }
@Override
public void visit(PiRoundResetEvent piRoundResetEvent) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(NewSessionEvent newSessionEvent) { }
@CacheEvict(value = "statistics", allEntries = true)
@Override
public void visit(DeleteSessionEvent deleteSessionEvent) { }
@Override
public void visit(LockVoteEvent lockVoteEvent) { }
@Override
public void visit(LockVotesEvent lockVotesEvent) { }
@Override
public void visit(UnlockVoteEvent unlockVoteEvent) { }
@Override
public void visit(UnlockVotesEvent unlockVotesEvent) { }
@Override
public void visit(FeatureChangeEvent featureChangeEvent) { }
@Override
public void visit(LockFeedbackEvent lockFeedbackEvent) { }
@Override
public void visit(FlipFlashcardsEvent flipFlashcardsEvent) { }
public interface CacheBuster {
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ARSnova Backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.cache;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import de.thm.arsnova.event.AfterCreationEvent;
import de.thm.arsnova.event.AfterDeletionEvent;
import de.thm.arsnova.event.ChangeScoreEvent;
import de.thm.arsnova.model.Answer;
import de.thm.arsnova.model.Comment;
import de.thm.arsnova.model.Room;
/**
* This class is used to evict caches based on events. The events carry all necessary information to clear the
* caches, e.g, for a specific session.
*/
@Component
public class CacheBusterImpl implements CacheBuster {
@CacheEvict(value = "statistics", allEntries = true)
@EventListener
public void handleAfterCommentCreation(final AfterCreationEvent<Comment> event) {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "statistics", allEntries = true)
@EventListener
public void handleAfterCommentDeletion(final AfterDeletionEvent<Comment> event) {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "answerlists", key = "#event.content.id")
@EventListener
public void handleAfterAnswerCreation(final AfterCreationEvent<Answer> event) {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "statistics", allEntries = true)
@EventListener
public void handleChangeScore(final ChangeScoreEvent event) {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "statistics", allEntries = true)
@EventListener
public void handleAfterRoomCreation(final AfterCreationEvent<Room> event) {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "statistics", allEntries = true)
@EventListener
public void handleAfterRoomDeletion(final AfterDeletionEvent<Room> event) {
/* Implementation provided by caching aspect. */
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -15,13 +15,16 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.cache;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/** This component cleares caches at fixed time intervals:
/** This component cleares caches at fixed time intervals.
*
* <p>Time intervals:
* <ul>
* <li><code>sessions</code>: 6h</li>
* <li><code>skillquestions</code>, <code>lecturequestions</code>, <code>preparationquestions</code>,
......@@ -30,40 +33,57 @@ import org.springframework.stereotype.Component;
* <li><code>answers</code>: 15min</li>
* <li><code>learningprogress</code>: 15min</li>
* </ul>
* </p>
*/
@Component
public class ScheduledCacheBuster {
@CacheEvict(value = "sessions", allEntries = true)
@CacheEvict(value = "rooms", allEntries = true)
@Scheduled(initialDelay = 1000 * 25, fixedRate = 1000 * 60 * 60 * 6)
private void clearSessionCache() { }
private void clearSessionCache() {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "questions", allEntries = true)
@CacheEvict(value = "contents", allEntries = true)
@Scheduled(initialDelay = 1000 * 50, fixedRate = 1000 * 60 * 30)
private void clearQuestionCache() { }
private void clearQuestionCache() {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "skillquestions", allEntries = true)
@CacheEvict(value = "contentlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 75, fixedRate = 1000 * 60 * 30)
private void clearSkillQuestionCache() { }
private void clearSkillQuestionCache() {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "lecturequestions", allEntries = true)
@CacheEvict(value = "lecturecontentlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 100, fixedRate = 1000 * 60 * 30)
private void clearLectureQuestionCache() { }
private void clearLectureQuestionCache() {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "preparationquestions", allEntries = true)
@CacheEvict(value = "preparationcontentlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 125, fixedRate = 1000 * 60 * 30)
private void clearPreparationQuestionCache() { }
private void clearPreparationQuestionCache() {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "flashcardquestions", allEntries = true)
@CacheEvict(value = "flashcardcontentlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 150, fixedRate = 1000 * 60 * 30)
private void clearFlashcardQuestionCache() { }
private void clearFlashcardQuestionCache() {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "answers", allEntries = true)
@CacheEvict(value = "answerlists", allEntries = true)
@Scheduled(initialDelay = 1000 * 175, fixedRate = 1000 * 60 * 15)
private void clearAnswerCache() { }
private void clearAnswerCache() {
/* Implementation provided by caching aspect. */
}
@CacheEvict(value = "learningprogress", allEntries = true)
@CacheEvict(value = "score", allEntries = true)
@Scheduled(initialDelay = 1000 * 200, fixedRate = 1000 * 60 * 15)
private void clearLearningProgressCache() { }
private void clearLearningProgressCache() {
/* Implementation provided by caching aspect. */
}
}
/**
* Classes and interfaces related to caching
* Classes and interfaces related to caching.
*/
package de.thm.arsnova.cache;
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -15,34 +15,37 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.events;
import de.thm.arsnova.entities.Question;
import de.thm.arsnova.entities.Session;
package de.thm.arsnova.config;
import java.util.Arrays;
import java.util.List;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
/**
* Fires whenever a set of questions are enabled, i.e., they become visible to students.
* An {@link AutoConfigurationImportFilter} that only selects AutoConfiguration
* classes needed for Spring Actuator.
*
* @author Daniel Gerhardt
*/
public class UnlockQuestionsEvent extends SessionEvent {
private static final long serialVersionUID = 1L;
private List<Question> questions;
public UnlockQuestionsEvent(Object source, Session session, List<Question> questions) {
super(source, session);
this.questions = questions;
}
public List<Question> getQuestions() {
return this.questions;
}
public class ActuatorAutoConfigurationFilter implements AutoConfigurationImportFilter {
private static final List<String> actuatorClassesPrefixes = Arrays.asList(new String[] {
"org.springframework.boot.actuate.autoconfigure.",
"org.springframework.boot.autoconfigure.web.servlet."});
@Override
public void accept(NovaEventVisitor visitor) {
visitor.visit(this);
public boolean[] match(final String[] autoConfigurationClasses,
final AutoConfigurationMetadata autoConfigurationMetadata) {
final boolean[] results = new boolean[autoConfigurationClasses.length];
for (int i = 0; i < results.length; i++) {
results[i] = autoConfigurationClasses[i] != null && checkPrefix(autoConfigurationClasses[i]);
}
return results;
}
private boolean checkPrefix(final String className) {
return actuatorClassesPrefixes.stream().anyMatch(p -> className.startsWith(p));
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -15,31 +15,36 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import de.thm.arsnova.ImageUtils;
import de.thm.arsnova.connector.client.ConnectorClient;
import de.thm.arsnova.connector.client.ConnectorClientImpl;
import de.thm.arsnova.socket.ARSnovaSocket;
import de.thm.arsnova.socket.ARSnovaSocketIOServer;
import de.thm.arsnova.web.CacheControlInterceptorHandler;
import de.thm.arsnova.web.CorsFilter;
import de.thm.arsnova.web.DeprecatedApiInterceptorHandler;
import de.thm.arsnova.web.ResponseInterceptorHandler;
import io.micrometer.core.instrument.MeterRegistry;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.CharEncoding;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
......@@ -54,96 +59,140 @@ import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.thm.arsnova.config.properties.FeatureProperties;
import de.thm.arsnova.config.properties.SecurityProperties;
import de.thm.arsnova.config.properties.SystemProperties;
import de.thm.arsnova.connector.client.ConnectorClient;
import de.thm.arsnova.connector.client.ConnectorClientImpl;
import de.thm.arsnova.model.migration.FromV2Migrator;
import de.thm.arsnova.model.migration.ToV2Migrator;
import de.thm.arsnova.model.serialization.CouchDbDocumentModule;
import de.thm.arsnova.model.serialization.View;
import de.thm.arsnova.util.ImageUtils;
import de.thm.arsnova.web.CacheControlInterceptorHandler;
import de.thm.arsnova.web.CorsFilter;
import de.thm.arsnova.web.DeprecatedApiInterceptorHandler;
import de.thm.arsnova.web.PathBasedContentNegotiationStrategy;
import de.thm.arsnova.web.ResponseInterceptorHandler;
import de.thm.arsnova.websocket.ArsnovaSocketioServer;
import de.thm.arsnova.websocket.ArsnovaSocketioServerImpl;
/**
* Loads property file and configures non-security related beans and components.
*
* <p>
* expose-proxy for AspectJ is needed to access the proxy object via AopContext.currentProxy() in CouchDBDao. It might
* have a negative impact on performance but is needed for caching until a better solution is implemented (e.g. use of
* AspectJ's weaving).
* </p>
*/
@ComponentScan({
"de.thm.arsnova.aop",
"de.thm.arsnova.cache",
"de.thm.arsnova.controller",
"de.thm.arsnova.domain",
"de.thm.arsnova.dao",
"de.thm.arsnova.events",
"de.thm.arsnova.event",
"de.thm.arsnova.management",
"de.thm.arsnova.security",
"de.thm.arsnova.services",
"de.thm.arsnova.web"})
"de.thm.arsnova.service",
"de.thm.arsnova.web",
"de.thm.arsnova.websocket.handler"})
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableAsync
@EnableCaching
@EnableAsync(mode = AdviceMode.ASPECTJ)
@EnableAutoConfiguration
@EnableCaching(mode = AdviceMode.ASPECTJ)
@EnableScheduling
@EnableSpringConfigured
@EnableWebMvc
@PropertySource(
value = {"classpath:arsnova.properties.example", "file:/etc/arsnova/arsnova.properties"},
ignoreResourceNotFound = true
value = {
"classpath:config/defaults.yml",
"classpath:config/actuator.yml",
"file:${arsnova.config-dir:.}/application.yml",
"file:${arsnova.config-dir:.}/secrets.yml",
"file:${arsnova.config-dir:.}/ui.yml"},
ignoreResourceNotFound = true,
encoding = CharEncoding.UTF_8,
factory = YamlPropertySourceFactory.class
)
public class AppConfig extends WebMvcConfigurerAdapter {
public static final MediaType API_V2_MEDIA_TYPE = new MediaType("application", "vnd.de.thm.arsnova.v2+json");
public static final MediaType API_V3_MEDIA_TYPE = new MediaType("application", "vnd.de.thm.arsnova.v3+json");
@EnableConfigurationProperties({
FeatureProperties.class,
SystemProperties.class})
public class AppConfig implements WebMvcConfigurer {
public static final String API_V2_MEDIA_TYPE_VALUE = "application/vnd.de.thm.arsnova.v2+json";
public static final String API_V3_MEDIA_TYPE_VALUE = "application/vnd.de.thm.arsnova.v3+json";
public static final MediaType API_V2_MEDIA_TYPE = MediaType.valueOf(API_V2_MEDIA_TYPE_VALUE);
public static final MediaType API_V3_MEDIA_TYPE = MediaType.valueOf(API_V3_MEDIA_TYPE_VALUE);
public static final MediaType ACTUATOR_MEDIA_TYPE = MediaType.valueOf(ActuatorMediaType.V2_JSON);
@Autowired
private Environment env;
@Value(value = "${connector.enable}") private boolean connectorEnable;
@Value(value = "${connector.uri}") private String connectorUri;
@Value(value = "${connector.username}") private String connectorUsername;
@Value(value = "${connector.password}") private String connectorPassword;
@Autowired
private SystemProperties systemProperties;
@Value(value = "${socketio.bind-address}") private String socketAddress;
@Value(value = "${socketio.port}") private int socketPort;
@Value(value = "${socketio.ssl.jks-file:}") private String socketKeystore;
@Value(value = "${socketio.ssl.jks-password:}") private String socketKeystorePassword;
@Value(value = "${security.cors.origins:}") private String[] corsOrigins;
@Value(value = "${mail.host}") private String mailHost;
@Value(value = "${api.indent-response-body:false}") private boolean apiIndent;
@Autowired
private SecurityProperties securityProperties;
@Autowired
private WebEndpointProperties webEndpointProperties;
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(stringMessageConverter());
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
converters.add(defaultJsonMessageConverter());
converters.add(apiV2JsonMessageConverter());
converters.add(managementJsonMessageConverter());
converters.add(stringMessageConverter());
//converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
final PathBasedContentNegotiationStrategy strategy =
new PathBasedContentNegotiationStrategy(API_V3_MEDIA_TYPE, webEndpointProperties.getBasePath());
configurer.mediaType("json", MediaType.APPLICATION_JSON_UTF8);
configurer.mediaType("xml", MediaType.APPLICATION_XML);
configurer.defaultContentType(API_V3_MEDIA_TYPE);
configurer.favorParameter(true);
configurer.favorParameter(false);
configurer.favorPathExtension(false);
//configurer.defaultContentType(API_V3_MEDIA_TYPE);
configurer.defaultContentTypeStrategy(strategy);
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
public void configurePathMatch(final PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
@Override
public void configureViewResolvers(final ViewResolverRegistry registry) {
registry.viewResolver(new InternalResourceViewResolver());
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(cacheControlInterceptorHandler());
registry.addInterceptor(deprecatedApiInterceptorHandler());
registry.addInterceptor(responseInterceptorHandler());
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger.json").addResourceLocations("classpath:/");
}
/* Provides a Spring Framework (non-Boot) compatible Filter. */
@Bean
public WebMvcMetricsFilter webMvcMetricsFilterOverride(
final MeterRegistry registry, final WebMvcTagsProvider tagsProvider) {
final MetricsProperties.Web.Server serverProperties = new MetricsProperties.Web.Server();
return new WebMvcMetricsFilter(registry, tagsProvider,
serverProperties.getRequestsMetricName(), serverProperties.isAutoTimeRequests());
}
@Bean
public CacheControlInterceptorHandler cacheControlInterceptorHandler() {
return new CacheControlInterceptorHandler();
......@@ -161,9 +210,12 @@ public class AppConfig extends WebMvcConfigurerAdapter {
@Bean
public StringHttpMessageConverter stringMessageConverter() {
StringHttpMessageConverter messageConverter = new StringHttpMessageConverter();
List<MediaType> mediaTypes = new ArrayList<>();
final StringHttpMessageConverter messageConverter = new StringHttpMessageConverter();
messageConverter.setDefaultCharset(StandardCharsets.UTF_8);
messageConverter.setWriteAcceptCharset(false);
final List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.TEXT_PLAIN);
mediaTypes.add(MediaType.APPLICATION_XML);
messageConverter.setSupportedMediaTypes(mediaTypes);
return messageConverter;
......@@ -171,14 +223,16 @@ public class AppConfig extends WebMvcConfigurerAdapter {
@Bean
public MappingJackson2HttpMessageConverter defaultJsonMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder
.serializationInclusion(JsonInclude.Include.NON_EMPTY)
.defaultViewInclusion(false)
.indentOutput(apiIndent)
.indentOutput(systemProperties.getApi().isIndentResponseBody())
.simpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
List<MediaType> mediaTypes = new ArrayList<>();
final ObjectMapper mapper = builder.build();
mapper.setConfig(mapper.getSerializationConfig().withView(View.Public.class));
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(mapper);
final List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(API_V3_MEDIA_TYPE);
mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(mediaTypes);
......@@ -188,15 +242,18 @@ public class AppConfig extends WebMvcConfigurerAdapter {
@Bean
public MappingJackson2HttpMessageConverter apiV2JsonMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder
.serializationInclusion(JsonInclude.Include.NON_EMPTY)
.serializationInclusion(JsonInclude.Include.NON_NULL)
.defaultViewInclusion(false)
.indentOutput(apiIndent)
.indentOutput(systemProperties.getApi().isIndentResponseBody())
.featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.featuresToEnable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
List<MediaType> mediaTypes = new ArrayList<>();
.featuresToEnable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
.modules(new CouchDbDocumentModule());
final ObjectMapper mapper = builder.build();
mapper.setConfig(mapper.getSerializationConfig().withView(View.Public.class));
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(mapper);
final List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(API_V2_MEDIA_TYPE);
mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(mediaTypes);
......@@ -204,6 +261,22 @@ public class AppConfig extends WebMvcConfigurerAdapter {
return converter;
}
@Bean
public MappingJackson2HttpMessageConverter managementJsonMessageConverter() {
final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder
.indentOutput(systemProperties.getApi().isIndentResponseBody())
.simpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
final ObjectMapper mapper = builder.build();
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(mapper);
final List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(ACTUATOR_MEDIA_TYPE);
mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(mediaTypes);
return converter;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
final PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
......@@ -214,7 +287,7 @@ public class AppConfig extends WebMvcConfigurerAdapter {
@Bean
public PropertiesFactoryBean versionInfoProperties() {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
final PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("version.properties"));
return propertiesFactoryBean;
......@@ -222,32 +295,30 @@ public class AppConfig extends WebMvcConfigurerAdapter {
@Bean
public CorsFilter corsFilter() {
return new CorsFilter(Arrays.asList(corsOrigins));
return new CorsFilter(securityProperties.getCorsOrigins());
}
@Bean(name = "connectorClient")
public ConnectorClient connectorClient() {
if (!connectorEnable) {
if (!systemProperties.getLmsConnector().isEnabled()) {
return null;
}
final ConnectorClientImpl connectorClient = new ConnectorClientImpl();
connectorClient.setServiceLocation(connectorUri);
connectorClient.setUsername(connectorUsername);
connectorClient.setPassword(connectorPassword);
connectorClient.setServiceLocation(systemProperties.getLmsConnector().getHostUrl());
connectorClient.setUsername(systemProperties.getLmsConnector().getUsername());
connectorClient.setPassword(systemProperties.getLmsConnector().getPassword());
return connectorClient;
}
@Profile("!test")
@Bean(name = "socketServer", initMethod = "startServer", destroyMethod = "stopServer")
public ARSnovaSocket socketServer() {
final ARSnovaSocketIOServer socketServer = new ARSnovaSocketIOServer();
socketServer.setHostIp(socketAddress);
socketServer.setPortNumber(socketPort);
socketServer.setUseSSL(!socketKeystore.isEmpty());
socketServer.setKeystore(socketKeystore);
socketServer.setStorepass(socketKeystorePassword);
return socketServer;
public ArsnovaSocketioServer socketServer() {
final ArsnovaSocketioServerImpl socketioServer = new ArsnovaSocketioServerImpl();
socketioServer.setHostIp(systemProperties.getSocketio().getBindAddress());
socketioServer.setPortNumber(systemProperties.getSocketio().getPort());
return socketioServer;
}
@Bean
......@@ -258,7 +329,7 @@ public class AppConfig extends WebMvcConfigurerAdapter {
@Bean
public JavaMailSenderImpl mailSender() {
final JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(mailHost);
mailSender.setHost(systemProperties.getMail().getHost());
return mailSender;
}
......@@ -267,4 +338,14 @@ public class AppConfig extends WebMvcConfigurerAdapter {
public ImageUtils imageUtils() {
return new ImageUtils();
}
@Bean
public FromV2Migrator fromV2Migrator() {
return new FromV2Migrator();
}
@Bean
public ToV2Migrator toV2Migrator() {
return new ToV2Migrator();
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ARSnova Backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.config;
import javax.servlet.Filter;
import javax.servlet.ServletRegistration;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {
AppConfig.class,
PersistenceConfig.class,
SecurityConfig.class,
WebSocketConfig.class,
};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[0];
}
@Override
protected String[] getServletMappings() {
return new String[] {
"/"
};
}
@Override
protected Filter[] getServletFilters() {
final CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter("UTF-8");
final DelegatingFilterProxy webMvcMetricsFilter = new DelegatingFilterProxy("webMvcMetricsFilterOverride");
final DelegatingFilterProxy corsFilter = new DelegatingFilterProxy("corsFilter");
final DelegatingFilterProxy maintenanceModeFilter = new DelegatingFilterProxy("maintenanceModeFilter");
final DelegatingFilterProxy v2ContentTypeOverrideFilter = new DelegatingFilterProxy("v2ContentTypeOverrideFilter");
return new Filter[] {
webMvcMetricsFilter,
characterEncodingFilter,
corsFilter,
maintenanceModeFilter,
v2ContentTypeOverrideFilter
};
}
@Override
protected void customizeRegistration(final ServletRegistration.Dynamic registration) {
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
@Override
protected String getServletName() {
return "arsnova";
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ARSnova Backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.config;
import org.ektorp.impl.StdCouchDbInstance;
import org.ektorp.spring.HttpClientFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import de.thm.arsnova.config.properties.CouchDbProperties;
import de.thm.arsnova.model.serialization.CouchDbObjectMapperFactory;
import de.thm.arsnova.persistence.AnswerRepository;
import de.thm.arsnova.persistence.CommentRepository;
import de.thm.arsnova.persistence.ContentGroupRepository;
import de.thm.arsnova.persistence.ContentRepository;
import de.thm.arsnova.persistence.LogEntryRepository;
import de.thm.arsnova.persistence.MotdRepository;
import de.thm.arsnova.persistence.RoomRepository;
import de.thm.arsnova.persistence.SessionStatisticsRepository;
import de.thm.arsnova.persistence.StatisticsRepository;
import de.thm.arsnova.persistence.UserRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbAnswerRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbCommentRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbContentGroupRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbContentRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbLogEntryRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbMotdRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbRoomRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbSessionStatisticsRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbStatisticsRepository;
import de.thm.arsnova.persistence.couchdb.CouchDbUserRepository;
import de.thm.arsnova.persistence.couchdb.support.MangoCouchDbConnector;
@ComponentScan({
"de.thm.arsnova.persistence.couchdb"
})
@Configuration
@EnableConfigurationProperties(CouchDbProperties.class)
@Profile("!test")
public class PersistenceConfig {
private static final int MIGRATION_SOCKET_TIMEOUT = 30000;
private CouchDbProperties properties;
public PersistenceConfig(final CouchDbProperties couchDbProperties) {
this.properties = couchDbProperties;
}
@Bean
@Primary
public MangoCouchDbConnector couchDbConnector() throws Exception {
return new MangoCouchDbConnector(properties.getDbName(), couchDbInstance(), couchDbObjectMapperFactory());
}
@Bean
@ConditionalOnProperty(
name = "migrate-from",
prefix = CouchDbProperties.PREFIX)
public MangoCouchDbConnector couchDbMigrationConnector() throws Exception {
return new MangoCouchDbConnector(properties.getMigrateFrom(), couchDbInstance(), couchDbObjectMapperFactory());
}
@Bean
public StdCouchDbInstance couchDbInstance() throws Exception {
return new StdCouchDbInstance(couchDbHttpClientFactory().getObject());
}
@Bean
@ConditionalOnProperty(
name = "migrate-from",
prefix = CouchDbProperties.PREFIX)
public StdCouchDbInstance couchDbMigrationInstance() throws Exception {
return new StdCouchDbInstance(couchDbMigrationHttpClientFactory().getObject());
}
@Bean
public HttpClientFactoryBean couchDbHttpClientFactory() throws Exception {
final HttpClientFactoryBean factory = new HttpClientFactoryBean();
factory.setHost(properties.getHost());
factory.setPort(properties.getPort());
if (!properties.getUsername().isEmpty()) {
factory.setUsername(properties.getUsername());
factory.setPassword(properties.getPassword());
}
return factory;
}
@Bean
@ConditionalOnProperty(
name = "migrate-from",
prefix = CouchDbProperties.PREFIX)
public HttpClientFactoryBean couchDbMigrationHttpClientFactory() throws Exception {
final HttpClientFactoryBean factory = couchDbHttpClientFactory();
factory.setSocketTimeout(MIGRATION_SOCKET_TIMEOUT);
return factory;
}
@Bean
public CouchDbObjectMapperFactory couchDbObjectMapperFactory() {
return new CouchDbObjectMapperFactory();
}
@Bean
public LogEntryRepository logEntryRepository() throws Exception {
return new CouchDbLogEntryRepository(couchDbConnector(), false);
}
@Bean
public UserRepository userRepository() throws Exception {
return new CouchDbUserRepository(couchDbConnector(), false);
}
@Bean
public RoomRepository sessionRepository() throws Exception {
return new CouchDbRoomRepository(couchDbConnector(), false);
}
@Bean
public CommentRepository commentRepository() throws Exception {
return new CouchDbCommentRepository(couchDbConnector(), false);
}
@Bean
public ContentRepository contentRepository() throws Exception {
return new CouchDbContentRepository(couchDbConnector(), false);
}
@Bean
public ContentGroupRepository contentGroupRepository() throws Exception {
return new CouchDbContentGroupRepository(couchDbConnector(), false);
}
@Bean
public AnswerRepository answerRepository() throws Exception {
return new CouchDbAnswerRepository(couchDbConnector(), false);
}
@Bean
public MotdRepository motdRepository() throws Exception {
return new CouchDbMotdRepository(couchDbConnector(), false);
}
@Bean
public StatisticsRepository statisticsRepository() throws Exception {
return new CouchDbStatisticsRepository(couchDbConnector(), false);
}
@Bean
public SessionStatisticsRepository sessionStatisticsRepository() throws Exception {
return new CouchDbSessionStatisticsRepository(couchDbConnector(), false);
}
}
package de.thm.arsnova.config;
import java.util.Properties;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.CollectionFactory;
public class PrefixedYamlPropertiesFactoryBean extends YamlPropertiesFactoryBean {
private static final String PREFIX = "arsnova";
@Override
protected Properties createProperties() {
final Properties result = CollectionFactory.createStringAdaptingProperties();
process((properties, map) -> properties.forEach((k, v) -> {
if (k.toString().startsWith(PREFIX + ".")) {
result.put(k.toString().substring(PREFIX.length() + 1), v);
}
}));
return result;
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ARSnova Backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
package de.thm.arsnova.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import de.thm.arsnova.config.properties.MessageBrokerProperties;
import de.thm.arsnova.config.properties.SecurityProperties;
import de.thm.arsnova.websocket.handler.AuthChannelInterceptorAdapter;
@Configuration
@EnableWebSocketMessageBroker
@EnableConfigurationProperties(MessageBrokerProperties.class)
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private static final String MESSAGING_PREFIX = "/backend";
private static final String[] DESTINATION_PREFIX = {"/exchange", "/topic", "/queue"};
private static final String USER_REGISTRY_BROADCAST = "/topic/log-user-registry";
private static final String USER_DESTINATION_BROADCAST = "/queue/log-unresolved-user";
private final MessageBrokerProperties.Relay relayProperties;
private final AuthChannelInterceptorAdapter authChannelInterceptorAdapter;
private String[] corsOrigins;
@Autowired
public WebSocketConfig(
final MessageBrokerProperties messageBrokerProperties,
final SecurityProperties securityProperties,
final AuthChannelInterceptorAdapter authChannelInterceptorAdapter) {
this.relayProperties = messageBrokerProperties.getRelay();
this.corsOrigins = securityProperties.getCorsOrigins().stream().toArray(String[]::new);
this.authChannelInterceptorAdapter = authChannelInterceptorAdapter;
}
@Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes(MESSAGING_PREFIX);
if (relayProperties.isEnabled()) {
config
.enableStompBrokerRelay(DESTINATION_PREFIX)
.setUserRegistryBroadcast(USER_REGISTRY_BROADCAST)
.setUserDestinationBroadcast(USER_DESTINATION_BROADCAST)
.setRelayHost(relayProperties.getHost())
.setRelayPort(relayProperties.getPort())
.setClientLogin(relayProperties.getUsername())
.setClientPasscode(relayProperties.getPassword());
} else {
config.enableSimpleBroker(DESTINATION_PREFIX);
}
}
@Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins(corsOrigins).withSockJS();
}
@Override
public void configureClientInboundChannel(final ChannelRegistration registration) {
registration.interceptors(authChannelInterceptorAdapter);
}
}