# Frage 1 - Annotations

**2 Punkte**

Implementieren Sie eine `@Range`-Annotation basierend auf der Verwendung im unten gegebenen Code.

Die Annotation soll über Reflection zur Laufzeit auslesbar und auf Feldern sowie Parametern erlaubt sein. Sie besitzt die verpflichtende Eigenschaft `min` und die optionale Eigenschaft `max`. Der Standardwert von `max` ist `Integer.MAX_VALUE`.

```java
class Registration {
    @Range(min = 1, max = 120)
    private int age;

    public void setGroupSize(@Range(min = 2) int groupSize) { ... }
}
```

Hilfreiche APIs:

```java
public enum ElementType {
    TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, ...
}

public enum RetentionPolicy {
    SOURCE, CLASS, RUNTIME
}
```

# Frage 2 - Reflection

**2 Punkte**

Welchen Text gibt das nachfolgende Programm auf die Konsole aus?

```java
class Box<T> { }
class StringBox extends Box<String> { }

class Main {
    public static void main(String[] args) {
        Class<?> arrayClass = StringBox[].class;

        System.out.print(arrayClass.isArray() + " ");
        System.out.print(arrayClass.getComponentType().getSimpleName() + " ");
        System.out.print(StringBox.class.getSuperclass().getSimpleName() + " ");
        System.out.print(StringBox.class.getTypeParameters().length);
    }
}
```

# Frage 3 - JavaFX Properties und Bindings

**4 Punkte**

Implementieren Sie die Bindings an der TODO-Stelle in der unten gegebenen Klasse.

- `registerButton` soll deaktiviert sein, wenn `nameField` leer oder der Wert des `ageSpinner` kleiner als 18 ist.
- `statusLabel` soll automatisch `"not eligible"` anzeigen, solange der Button deaktiviert ist, und sonst
  `"ready"`.
- Verwenden Sie ausschließlich Bindings. Event-Handler und Change-Listener sind nicht erlaubt.

```java
class RegistrationController {
    @FXML public TextField nameField;
    @FXML public Spinner<Integer> ageSpinner;
    @FXML public Button registerButton;
    @FXML public Label statusLabel;

    @FXML
    public void initialize() {
        // TODO
    }
}
```

Hilfreiche APIs:

```java
public abstract class StringExpression {
    public BooleanBinding isEmpty();
}

public abstract class BooleanExpression {
    public BooleanBinding or(ObservableBooleanValue other);
}

public final class Bindings {
    public static When when(ObservableBooleanValue condition);
    public static BooleanBinding createBooleanBinding(
            Callable<Boolean> function,
            Observable... dependencies);
}

public final class When {
    public <T> Then<T> then(T value);
}

public final class Then<T> {
    public ObjectBinding<T> otherwise(T value);
}
```

# Frage 4 - Parallele Ausführung

Aus Übung 4 zur parallelen Ausführung mit dem Fork/Join-Framework ist gegeben:

```java
public interface Problem<R, P extends Problem<R, P>> {
    boolean isSmall();
    List<P> split();
    R combine(List<R> results);
    R solveSequential();
}
```

Die bereits implementierte Klasse `ProblemRecursiveTask` führt ein solches Problem mit dem Fork/Join-Framework aus. Ihre Implementierung ist für diese Aufgabe nicht relevant.

## 4.1 LongestLineProblem

**5 Punkte**

Implementieren Sie eine Klasse `LongestLineProblem`, die das Interface `Problem` implementiert. Das Ergebnis wird durch folgenden Record dargestellt:

```java
public record LineResult(int index, int length) { }
```

Ein `LongestLineProblem` bearbeitet den Bereich `[from, to)` einer `List<String>`.

- Das Resultat enthält Index und Länge der längsten Zeile im Bereich.
- Bei gleich langen Zeilen gewinnt der kleinere Index.
- Bei weniger als 20 Zeilen wird sequentiell gearbeitet.
- Sonst wird der Bereich in der Mitte in zwei Teilprobleme geteilt.
- `combine` wählt aus den Teilresultaten das bessere Resultat.

Sie können annehmen, dass der Bereich nie leer ist. Schreiben Sie die vollständige Klasse mit Feldern, Konstruktor und allen Methoden des Interfaces.

# Frage 5 - Non-blocking Server mit Selector

Ein Server verwaltet nicht-blockierende Client-Verbindungen. Jeder Client besitzt einen `ClientState`, der als Attachment am zugehörigen `SelectionKey` gespeichert wird. Zusätzlich werden die Keys nach Channel und angemeldete Clients nach Benutzername in Maps verwaltet.

```java
private static void acceptClient(
        ServerSocketChannel server,
        Selector selector,
        Map<SocketChannel, SelectionKey> channelKeys)
        throws IOException {

    SocketChannel channel = server.accept();
    channel.configureBlocking(false);

    ClientState state = new ClientState(channel);

    // TODO 5.1:
    // Register channel for read events and attach state

    channelKeys.put(channel, key);
}

private static void closeClient(
        SelectionKey key,
        Map<SocketChannel, SelectionKey> channelKeys,
        Map<String, ClientState> loggedInClients)
        throws IOException {

    // TODO 5.2:
    // Remove client from both maps, cancel key and close channel
}
```

## 5.1 Client registrieren

**1 Punkt**

Registrieren Sie den neuen `channel` für `OP_READ` beim `selector`. Speichern Sie den zurückgegebenen Key in einer Variable `key` und hängen Sie `state` als Attachment an diesen Key.

## 5.2 Client schließen

**2 Punkte**

Holen Sie den `ClientState` aus dem Attachment. Falls der Client bereits einen Namen besitzt, entfernen Sie ihn aus `loggedInClients`. Entfernen Sie außerdem den Channel aus `channelKeys`, brechen Sie den Key ab und schließen Sie den Channel.

## API-Auszug

```java
public abstract class SelectionKey {
    public static final int OP_READ;
    public abstract SelectableChannel channel();
    public final Object attachment();
    public final Object attach(Object object);
    public abstract void cancel();
}

public abstract class SocketChannel {
    public final SelectionKey register(Selector selector, int operations)
            throws IOException;
    public abstract SocketChannel configureBlocking(boolean block)
            throws IOException;
    public abstract void close() throws IOException;
}

public final class ClientState {
    public ClientState(SocketChannel channel);
    public String name();
}
```

# Frage 6 - Websockets

**4 Punkte**

Nehmen Sie an, die Klasse `Notification` sowie passende Klassen `NotificationDecoder` und `NotificationEncoder` sind bereits implementiert.

Vervollständigen Sie die vier mit `TODO` markierten Stellen (`a` bis `d`) des annotationsbasierten Client-Endpoints.

- Encoder und Decoder werden am Endpoint konfiguriert.
- Beim Verbindungsaufbau wird die `Session` gespeichert.
- Empfangene `Notification`-Objekte werden an den Listener weitergereicht.
- `send` überträgt eine `Notification` asynchron als typisierte Nachricht.

```java
@ClientEndpoint(/* TODO a */)
public final class NotificationEndpoint {
    private final Consumer<Notification> listener;
    private Session session;

    public NotificationEndpoint(Consumer<Notification> listener) {
        this.listener = listener;
    }

    @OnOpen
    public void onOpen(Session session) {
        // TODO b
    }

    @OnMessage
    public void onMessage(Notification notification) {
        // TODO c
    }

    public void send(Notification notification) {
        // TODO d
    }
}
```

Hilfreiche APIs:

```java
public interface Session {
    RemoteEndpoint.Async getAsyncRemote();
}

public interface RemoteEndpoint.Async {
    Future<Void> sendObject(Object data);
}

public interface Consumer<T> {
    void accept(T value);
}
```

# Frage 7 - Foreign Function & Memory

**4 Punkte**

Die native C-Funktion `uppercase_ascii` wandelt alle ASCII-Kleinbuchstaben in einem nullterminierten String direkt im übergebenen Speicher in Großbuchstaben um. Sie gibt die Anzahl der veränderten Zeichen zurück.

```c
long uppercase_ascii(char *text);
```

```java
String input = "Hello from Java";

try (Arena arena = Arena.ofConfined()) {
    SymbolLookup library = SymbolLookup.libraryLookup(
            Paths.get("textlib.dll"), arena);
    MemorySegment function = library.findOrThrow("uppercase_ascii");

    // TODOs der Teilaufgaben 7.a bis 7.d
}
```

## 7.a Speicher reservieren

**1 Punkt**

Reservieren Sie im `arena` einen Speicherbereich, der mit `input` als nullterminierter UTF-8-String initialisiert ist. Speichern Sie das `MemorySegment` in `text`.

## 7.b Funktionsbeschreibung

**1 Punkt**

Erstellen Sie den passenden `FunctionDescriptor`. Der C-`long` wird auf der verwendeten Plattform durch `ValueLayout.JAVA_LONG` beschrieben.

## 7.c Aufruf

**1 Punkt**

Erzeugen Sie einen Downcall-`MethodHandle`, rufen Sie die Funktion mit dem richtigen Argument auf und speichern Sie die Anzahl der Änderungen in `long changed`.

## 7.d Ergebnis lesen

**1 Punkt**

Lesen Sie den veränderten nullterminierten String aus `text` in `String result`.

Hilfreiche APIs:

```java
public interface SegmentAllocator {
    MemorySegment allocateFrom(String string);
}

public interface MemorySegment {
    String getString(long offset);
}

public final class FunctionDescriptor {
    public static FunctionDescriptor of(
            MemoryLayout result, MemoryLayout... arguments);
}

public interface Linker {
    static Linker nativeLinker();
    MethodHandle downcallHandle(
            MemorySegment symbol, FunctionDescriptor descriptor);
}

public final class ValueLayout {
    public static final OfLong JAVA_LONG;
    public static final AddressLayout ADDRESS;
}
```
