Aufgabe 1: Erste Abgabe

ChaosLL1337 2026-10-07 14:49:53 +02:00
commit 2f6877af39
15 changed files with 790 additions and 0 deletions

10
.idea/.gitignore vendored 100644
View File

@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

13
.idea/compiler.xml 100644
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="vs.pu01" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/vs.pu01/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/vs.pu01/src/main/resources" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

15
.idea/misc.xml 100644
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="KubernetesApiProvider"><![CDATA[{}]]></component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/vs.pu01/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="temurin-25 (2)" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/vs.pu01.iml" filepath="$PROJECT_DIR$/.idea/vs.pu01.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$" />
</component>
</module>

10
vs.pu01/.gitignore vendored 100644
View File

@ -0,0 +1,10 @@
**/.DS_Store
**/*.class
/bin/
/.project
/.classpath
/.factorypath
/.settings/
/target/
/.apt_generated/
/.apt_generated_tests/

12
vs.pu01/justfile 100644
View File

@ -0,0 +1,12 @@
default:
mvn clean compile test
exec class args: compile
mvn exec:java -Dexec.mainClass={{class}} -Dexec.args={{args}}
clean:
mvn clean
compile:
mvn compile
test: compile
mvn test
javadoc:
mvn javadoc:javadoc

60
vs.pu01/pom.xml 100644
View File

@ -0,0 +1,60 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>vs</groupId>
<artifactId>vs.pu01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.release>10</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency><!-- für Unit-Tests -->
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency><!-- für Lombok -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.48</version>
<scope>provided</scope>
</dependency>
<dependency><!-- für net.jcip Annotationen -->
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin><!-- für Unit-Tests [mvn test] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin><!-- [mvn compile] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
</plugin>
<plugin><!-- [mvn exec:java] -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin><!-- [mvn javadoc:javadoc] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<show>private</show>
<locale>en_US</locale>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,87 @@
package vs;
/**
* helper class for RFC 5424 (https://datatracker.ietf.org/doc/html/rfc5424)
* compliant log messages as immutable Java objects - representation of a subset
* of printable strings of specific length
*
* @author Sandro Leuchter
*
*/
public abstract class AsciiChars {
private final String value;
public String value() {
return this.value;
}
protected AsciiChars(int length, String value) {
if (value != null) {
if (value.length() > length) {
throw new IllegalArgumentException(
"Stringlänge = " + value.length() + " > " + length
);
}
for (int c : value.getBytes()) {
if (c < 33 || c > 126) {
throw new IllegalArgumentException(
"Stringinhalt nicht printable US-ASCII ohne Space"
);
}
}
}
this.value = value;
}
@Override
public String toString() {
if (value() == null || value().length() == 0) {
return "-";
} else {
return value();
}
}
public static final class L004 extends AsciiChars {
public L004(String value) {
super(4, value);
}
}
public static final class L012 extends AsciiChars {
public L012(String value) {
super(12, value);
}
}
public static final class L032 extends AsciiChars {
public L032(String value) {
super(32, value);
}
}
public static final class L048 extends AsciiChars {
public L048(String value) {
super(48, value);
}
}
public static final class L128 extends AsciiChars {
public L128(String value) {
super(128, value);
}
}
public static final class L255 extends AsciiChars {
public L255(String value) {
super(255, value);
}
}
}

View File

@ -0,0 +1,180 @@
package vs;
import java.util.ArrayList;
import java.util.List;
/**
* helper class for RFC 5424 (https://datatracker.ietf.org/doc/html/rfc5424)
* compliant log messages as immutable Java objects - structured data (set of
* key/value-pairs) with some predefined sets according to the standard
*
* @author Sandro Leuchter
*
*/
public class StructuredData {
public static class Element {
private final String name;
private List<Param> parameters;
public static Element newTimeQuality(
boolean tzKnown,
boolean isSynced
) {
return newTimeQuality(tzKnown, isSynced, null);
}
public static Element newTimeQuality(
boolean tzKnown,
boolean isSynced,
Integer syncAccuracy
) {
var e = new Element("timeQuality");
e.add(new Param("tzKnown", (tzKnown) ? "1" : "0"));
e.add(new Param("isSynced", (isSynced) ? "1" : "0"));
if (syncAccuracy != null && !isSynced) {
e.add(new Param("syncAccuracy", String.valueOf(syncAccuracy)));
}
return e;
}
public static Element newOrigin(
String enterpriseId,
String software,
String swVersion
) {
return newOrigin(
new String[] {},
enterpriseId,
software,
swVersion
);
}
public static Element newOrigin(
String ip,
String enterpriseId,
String software,
String swVersion
) {
return newOrigin(
new String[] { ip },
enterpriseId,
software,
swVersion
);
}
public static Element newOrigin(
String[] ip,
String enterpriseId,
String software,
String swVersion
) {
var e = new Element("origin");
for (var p : ip) {
e = e.add(new Param("ip", p));
}
if (enterpriseId != null && !enterpriseId.equals("")) {
e = e.add(new Param("enterpriseId", enterpriseId));
}
if (software != null && !software.equals("")) {
e = e.add(new Param("software", software));
}
if (swVersion != null && !swVersion.equals("")) {
e = e.add(new Param("swVersion", swVersion));
}
return e;
}
public static Element newMeta(
Integer sequenceId,
Integer sysUpTime,
String language
) {
var e = new Element("meta");
if (sequenceId != null && sequenceId > 0) {
e = e.add(
new Param(
"sequenceId",
String.valueOf(sequenceId % 2147483647)
)
);
}
if (sysUpTime != null && sysUpTime >= 0) {
e = e.add(new Param("sysUpTime", String.valueOf(sysUpTime)));
}
if (language != null && !language.equals("")) {
e = e.add(new Param("language", language));
}
return e;
}
public Element(String name) {
this.name = name;
this.parameters = new ArrayList<>();
}
public Element add(Param parameter) {
var e = new Element(this.name);
e.parameters = this.parameters;
e.parameters.add(parameter);
return e;
}
@Override
public String toString() {
var str = "[" + this.name;
for (var p : this.parameters) {
str = str + " " + p.toString();
}
return str + "]";
}
}
public static class Param {
private final String name;
// name: printable US-ASCII string ^[@=\]\"\s]+
// "@" + private enterpise number "@\d+(\.\d+)*"
private final String value;
public Param(String name, String value) {
this.name = name; // 7-Bit ASCII
this.value = value; // UTF-8
}
@Override
public String toString() {
return this.name + "=\"" + this.value + "\"";
}
}
private List<Element> params;
public StructuredData() {
this.params = new ArrayList<>();
}
public StructuredData(List<Element> params) {
this.params = params;
}
public String toString() {
if (this.params.size() == 0) {
return "-";
}
var str = "";
for (var p : this.params) {
str = str + p.toString();
}
return str;
}
public StructuredData add(Element e) {
var p = this.params;
p.add(e);
return new StructuredData(p);
}
}

View File

@ -0,0 +1,213 @@
package vs;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* RFC 5424 (https://datatracker.ietf.org/doc/html/rfc5424) compliant log
* messages as immutable Java objects
*
* @author Sandro Leuchter
*
*/
public class SyslogMessage implements Serializable {
private static final long serialVersionUID = -5895573029109990861L;
private final Facility fac;
private final Severity sev;
private final AsciiChars.L255 host;
private final AsciiChars.L048 appName;
private final AsciiChars.L128 procId;
private final AsciiChars.L032 msgId;
private final StructuredData data;
private final Message message;
public SyslogMessage(
Facility fac,
Severity sev,
AsciiChars.L255 host,
AsciiChars.L048 appName,
AsciiChars.L128 procId,
AsciiChars.L032 msgId,
StructuredData data,
Message message
) {
this.fac = fac;
this.sev = sev;
this.host = host;
this.appName = appName;
this.procId = procId;
this.msgId = msgId;
this.data = data;
this.message = message;
}
public Facility fac() {
return this.fac;
}
public Severity sev() {
return sev;
}
public AsciiChars.L255 host() {
return host;
}
public AsciiChars.L048 appName() {
return appName;
}
public AsciiChars.L128 procId() {
return procId;
}
public AsciiChars.L032 msgId() {
return msgId;
}
public StructuredData data() {
return data;
}
public Message message() {
return message;
}
public static int version() {
return VERSION;
}
public static enum Facility {
KERNEL,
USER,
MAIL_SYSTEM,
SYS_DAEMON,
SECURITY1,
INTERNAL,
PRINTER,
NEWS,
UUCP,
CLOCK1,
SECURITY2,
FTP,
NTP,
AUDIT,
ALERT,
CLOCK2,
LOCAL0,
LOCAL1,
LOCAL2,
LOCAL3,
LOCAL4,
LOCAL5,
LOCAL6,
LOCAL7,
}
public static enum Severity {
EMERGENCY,
ALERT,
CRITICAL,
ERROR,
WARNING,
NOTICE,
INFORMATIONAL,
DEBUG,
}
public static interface Message {
public Object message();
public int length();
}
public static class BinaryMessage implements Message {
private Byte[] message;
public BinaryMessage(Byte[] message) {
this.message = message;
}
@Override
public String toString() {
return message.toString();
}
@Override
public Object message() {
return this.message;
}
@Override
public int length() {
return this.message.length;
}
}
public static class TextMessage implements Message {
private String message; // UTF8
public TextMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "\u00EF\u00BB\u00BF" + message.toString();
}
@Override
public Object message() {
return this.message;
}
@Override
public int length() {
return this.message.length();
}
}
static final int VERSION = 1; // RFC 5424, Mar 2009
@Override
public String toString() {
var prival = String.valueOf(fac().ordinal() * 8 + sev().ordinal());
var d = "";
if (data() != null) {
d = " " + data();
}
var m = "";
if (
message() != null &&
message().message() != null &&
message().length() > 0
) {
m = " " + message();
}
var timestamp = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(
new Date()
);
return (
"<" +
prival +
">" +
VERSION +
" " +
timestamp +
" " +
host().toString() +
" " +
appName().toString() +
" " +
procId().toString() +
" " +
msgId().toString() +
d +
m
);
}
}

View File

@ -0,0 +1,56 @@
package vs;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class AsciiCharsTest {
@Test
void nullValue() {
var ac = new AsciiChars.L004(null);
assertEquals(ac.toString(), "-");
}
@Test
void emptyValue() {
var ac = new AsciiChars.L004("");
assertEquals(ac.toString(), "-");
}
@Test
void longValue() {
var ac = new AsciiChars.L004("1234");
assertEquals(ac.toString(), "1234");
}
@Test
void longerValue() {
var thrown = assertThrows(IllegalArgumentException.class, () -> {
new AsciiChars.L004("12345");
});
assertEquals("Stringlänge = 5 > 4", thrown.getMessage());
}
@Test
void space() {
var thrown = assertThrows(IllegalArgumentException.class, () -> {
new AsciiChars.L004("1 1");
});
assertEquals(
"Stringinhalt nicht printable US-ASCII ohne Space",
thrown.getMessage()
);
}
@Test
void special() {
var thrown = assertThrows(IllegalArgumentException.class, () -> {
new AsciiChars.L004("ä");
});
assertEquals(
"Stringinhalt nicht printable US-ASCII ohne Space",
thrown.getMessage()
);
}
}

View File

@ -0,0 +1,93 @@
package vs;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import vs.StructuredData.*;
import vs.SyslogMessage.*;
class SyslogMessageTest {
@Test
void testToString() {
var m1 = new SyslogMessage(
//
Facility.SECURITY1, //
Severity.CRITICAL, //
new AsciiChars.L255("mymachine.example.com"), //
new AsciiChars.L048("su"), //
new AsciiChars.L128(""), //
new AsciiChars.L032("ID47"), //
new StructuredData() //
.add(Element.newTimeQuality(true, true))
.add(
new Element("exampleSDID@32473")
.add(new Param("iut", "3"))
.add(new Param("eventSource", "Application"))
.add(new Param("eventID", "1011"))
)
.add(
new Element("examplePriority@32473").add(
new Param("class", "high")
)
),
new TextMessage("'su root' failed for lonvick on /dev/pts/8")
);
var s = m1.toString();
assertEquals(s.substring(0, 6), "<34>1 ");
assertEquals(
s.substring(s.length() - 221, s.length()),
" mymachine.example.com su - ID47 [timeQuality tzKnown=\"1\" isSynced=\"1\"][exampleSDID@32473 iut=\"3\" eventSource=\"Application\" eventID=\"1011\"][examplePriority@32473 class=\"high\"] 'su root' failed for lonvick on /dev/pts/8"
);
}
@Test
void test2() {
var m1 = new SyslogMessage(
//
Facility.SECURITY1, //
Severity.CRITICAL, //
new AsciiChars.L255("mymachine.example.com"), //
new AsciiChars.L048("su"), //
new AsciiChars.L128(""), //
new AsciiChars.L032("ID47"), //
null, //
new TextMessage("'su root' failed for lonvick on /dev/pts/8")
);
var s = m1.toString();
assertEquals(s.substring(0, 6), "<34>1 ");
assertEquals(
s.substring(s.length() - 78, s.length()),
" mymachine.example.com su - ID47 'su root' failed for lonvick on /dev/pts/8"
);
}
@Test
void test3() {
var m1 = new SyslogMessage(
//
Facility.SECURITY1, //
Severity.CRITICAL, //
new AsciiChars.L255("mymachine.example.com"), //
new AsciiChars.L048("su"), //
new AsciiChars.L128(""), //
new AsciiChars.L032("ID47"), //
new StructuredData() //
.add(Element.newTimeQuality(true, true))
.add(
Element.newOrigin(
new String[] { "0.0.8.8", "8.8.8.8" },
null,
null,
null
)
)
.add(Element.newMeta(null, 32, "de")),
new BinaryMessage(null)
);
assertEquals(
m1.data().toString(),
"[timeQuality tzKnown=\"1\" isSynced=\"1\"][origin ip=\"0.0.8.8\" ip=\"8.8.8.8\"][meta sysUpTime=\"32\" language=\"de\"]"
);
}
}