Skip to content

Commit de36370

Browse files
committed
Reactor docs + launcher fixes + minor pom polish (pre-release v00.10.00)
- README.md: new "Using the calendar component in your own Vaadin app" walkthrough (dependency snippet, route-wrapper code, three injection seams, customisation, styling); architecture tree rewritten for the 3-module reactor; all path references migrated to new module form (calendar-caldav: …, calendar-component: …, demo/src/…). - docs/CALENDAR_COMPONENT_EXTRACTION.md: post-hoc design rationale — architecture decisions, final module shape, three integration seams, phase-by-phase journey notes, deliberate out-of-scope list. - start-caldav-dev-server.sh: -pl calendar-caldav + new FQN junit.com.svenruppert.vaadin.calendar.CalDavDevServer. - start-vaadin-demo.sh: -pl demo so exec:java scopes to the demo submodule instead of failing against the pom-packaging reactor root. - demo/pom.xml: project name typo fix. - .gitignore: cover the reactor-relative paths (demo/src/main/frontend/generated, demo/src/main/bundles, demo/node_modules, demo/data).
1 parent d0ddd37 commit de36370

6 files changed

Lines changed: 420 additions & 72 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ demo/node_modules/
4848
### jSentinel persistence — Eclipse-Store data dir, bootstrap token ###
4949
/data/
5050
/Appl-iCloud-Password.txt
51+
/demo/data/

README.md

Lines changed: 187 additions & 68 deletions
Large diffs are not rendered by default.

demo/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
</organization>
2424

2525
<url>https://github.com/Java-Publications/Blog---Vaadin---Calendar-and-CalDav</url>
26-
<name>Core Vaadin Project Template</name>
26+
<name>Calendaar - CalDav Demo</name>
2727
<description>The beginning of your OSS project</description>
2828
<scm>
2929
<url>https://github.com/Java-Publications/Blog---Vaadin---Calendar-and-CalDav</url>
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Calendar component extraction — design rationale
2+
3+
This document captures the *why* and *how* of moving `CalendarView`
4+
from a single-module demo view to a publishable Vaadin Flow add-on
5+
spanning two artefacts. Written post-hoc with hindsight from running
6+
all three phases end-to-end.
7+
8+
For status / scope of future evolutions, see
9+
[`FEATURE_BACKLOG.md`](FEATURE_BACKLOG.md).
10+
11+
## 1. Goal
12+
13+
Originally `CalendarView` was hardwired into this specific project:
14+
host-specific routing annotations, host-specific i18n interface,
15+
direct `VaadinSession.getAttribute(...)` for persistence, dependency
16+
on a static `CalendarServiceProvider` holder, custom CSS in the
17+
host's `frontend/` tree. Extracting it as a drop-in Vaadin composite
18+
the rest of the world can `dependency`-pull was the goal.
19+
20+
Target shape:
21+
22+
```xml
23+
<dependency>
24+
<groupId>com.svenruppert.vaadin.calendar</groupId>
25+
<artifactId>calendar-component</artifactId>
26+
<version>00.10.00</version>
27+
</dependency>
28+
```
29+
30+
Mount it in any Vaadin app with a thin host-side wrapper carrying
31+
that app's own `@Route` + permission annotations.
32+
33+
## 2. Architecture decisions
34+
35+
Four load-bearing calls Sven took before Phase 1 started (preserved
36+
in [`memory/project_calendar_component_extraction`](../.claude/projects/.../memory/project_calendar_component_extraction.md)):
37+
38+
| # | Decision | Rationale |
39+
|---|---|---|
40+
| **Modules** | Two Maven artefacts: `calendar-caldav` (headless) and `calendar-component` (Vaadin) | Headless CLI / sync-job consumers can pull only the wire layer; the UI tree stays separable. |
41+
| **Error API** | `Result<T, CalDavError>` stays the public service-boundary type | Already runs through the codebase; `try/catch` is uglier and loses the typed error domain. |
42+
| **Coordinates** | `com.svenruppert.vaadin.calendar` namespace | Consistent with `com.svenruppert:functional-reactive`, `com.svenruppert:caldav-testbench`. |
43+
| **Presets** | `CalDavProviderPreset.DEFAULTS` (incl. Apple iCloud) bundled in the artefact | Quick-connect out-of-the-box; consumers can still inject additional presets via the config. |
44+
45+
## 3. Final module shape
46+
47+
```
48+
caldav-demo-reactor (pom — reactor root)
49+
├── calendar-caldav (jar)
50+
│ └── com.svenruppert.vaadin.calendar
51+
│ ├── client/ CalDavClient · CalDavDiscovery · CalDavError(s) · RemoteEvent
52+
│ ├── mapping/ EntryMapper (VEVENT/VTODO ↔ Entry)
53+
│ ├── service/ CalendarService · CalDavConnectionConfig
54+
│ │ CalDavServerConnection · CalendarSubscription
55+
│ │ CalDavProviderPreset
56+
│ ├── state/ CalendarStateStore (interface)
57+
│ └── i18n/ CalendarMessages (interface)
58+
59+
├── calendar-component (jar — Vaadin Flow add-on)
60+
│ └── com.svenruppert.vaadin.calendar
61+
│ ├── ui/ CalendarView · CalendarNavigationBar
62+
│ │ ConnectionStatusBadge · ConnectionsDialog
63+
│ │ EventEditorDialog · ServerStatusList
64+
│ │ SubscriptionsDialog
65+
│ └── state/ VaadinSessionCalendarStateStore (default impl)
66+
│ └── META-INF/resources/frontend/styles/calendar-view.css
67+
68+
└── demo (war — the consuming application)
69+
└── com.svenruppert.flow
70+
├── … (jSentinel, MainLayout, AppShell, all non-calendar views)
71+
└── views/CalendarRouteView @Route("calendar"), @VisibleFor(USER),
72+
layout = MainLayout.class
73+
↓ embeds
74+
new CalendarView(store, messages)
75+
```
76+
77+
The host (`demo`) carries every project-specific concern; both
78+
add-on artefacts are host-neutral.
79+
80+
## 4. Three integration seams
81+
82+
The published `calendar-component` exposes exactly three
83+
constructor-injected concerns:
84+
85+
### 4.1 `CalendarStateStore`
86+
87+
Persists Connection, Servers, Subscriptions, and the N-days slider
88+
between navigations. The default
89+
`VaadinSessionCalendarStateStore` keeps everything on the current
90+
session (the legacy behaviour). Plug a database-backed impl for
91+
cross-session continuity, or an in-memory stub for tests.
92+
93+
```java
94+
public interface CalendarStateStore {
95+
Optional<CalDavConnectionConfig> readConnection();
96+
void writeConnection(CalDavConnectionConfig cfg);
97+
98+
List<CalDavServerConnection> readServers();
99+
void writeServers(List<CalDavServerConnection> servers);
100+
101+
List<CalendarSubscription> readSubscriptions();
102+
void writeSubscriptions(List<CalendarSubscription> subs);
103+
104+
int readNDays(int fallback);
105+
void writeNDays(int n);
106+
}
107+
```
108+
109+
### 4.2 `CalendarMessages`
110+
111+
The i18n seam. A single functional method
112+
`tr(key, fallback, args...)`. Pass `this::tr` from your host's own
113+
i18n interface, or `CalendarMessages.fallbackOnly()` for an
114+
English-only build.
115+
116+
### 4.3 `CalendarService` *(optional)*
117+
118+
A pre-built service instance. Omit and the view bootstraps from the
119+
store's connection config, falling back to
120+
`CalDavProviderPreset.DEFAULTS.get(0)` (Apple iCloud quick-connect).
121+
122+
## 5. The phases, retold
123+
124+
### Phase 1 — In-place decouple
125+
126+
Stayed inside the single module. Introduced the
127+
`CalendarStateStore` and `CalendarMessages` interfaces, threaded
128+
them through `CalendarView` + every sub-component, moved
129+
`@Route` / `@VisibleFor` to a new `CalendarRouteView` wrapper.
130+
Replaced the host's `PageHeader` brick with a local
131+
`.calendar-view__page-header` div + CSS. Kept
132+
`CalendarServiceProvider` for the test-injection shim,
133+
encapsulated in `hostShimmedDefaultService()`.
134+
135+
Verification: 267 → 284 tests (the new abstractions ship with their
136+
own coverage), all green. SpotBugs 0 findings.
137+
138+
### Phase 2 — Multi-module reactor
139+
140+
Root pom becomes `packaging=pom` reactor. Existing source moves
141+
into `demo/`. Two new modules `calendar-caldav/` and
142+
`calendar-component/` get carved out by file-rename + dependency
143+
plumbing. `CalendarServiceProvider` deleted; the
144+
`CalendarViewBrowserlessTest` injection mechanism switches from
145+
`Provider.setService(testService)` to seeding the session
146+
connection config in `@BeforeEach`.
147+
148+
The split-package gotcha (`com.svenruppert.flow.views.*` lived in
149+
both demo and calendar-component) was resolved by giving the
150+
`calendar-component` its own UI package layer
151+
(`com.svenruppert.vaadin.calendar.ui.*`).
152+
153+
Verification: 66 + 5 + 213 = 284 tests across the reactor, all
154+
green. SpotBugs 0 each module.
155+
156+
### Phase 3 — Cleanup + namespace + publish prep
157+
158+
Trailing whitespace stripped from the EUPL headers that earlier
159+
license-plugin runs had baked in; `maven-checkstyle-plugin`
160+
re-enabled with 0 violations on both add-on modules.
161+
`license-maven-plugin` stays disabled in the add-on modules — it
162+
would otherwise re-inject the long header on every build.
163+
164+
The full namespace move from `com.svenruppert.flow.*` to
165+
`com.svenruppert.vaadin.calendar.*` ran via three `perl -i -pe`
166+
passes — bulk-rewriting package declarations and imports across
167+
the two new modules and threading through the demo. The
168+
`CalendarViewBrowserlessTest` package and physical location
169+
followed.
170+
171+
Sources + Javadoc jars wired up via explicit
172+
`maven-source-plugin` + `maven-javadoc-plugin` executions in each
173+
add-on pom — the publishable triplet now drops into
174+
`target/` on every `mvn install`. `distributionManagement`
175+
(s01.oss.sonatype.org) and `maven-gpg-plugin` are inherited from
176+
the parent `com.svenruppert:dependencies`.
177+
178+
`README.md` for each add-on module covers coordinates, package
179+
map, minimal mount snippet, and customisation seams.
180+
181+
## 6. What stayed deliberately out of scope
182+
183+
- **Maven Central deploy ceremony** — interactive: Sonatype creds
184+
+ GPG passphrase. The pom is wired; the push is a Sven action.
185+
- **Per-entry colour + calendar stripe** — parked in
186+
[`FEATURE_BACKLOG.md`](FEATURE_BACKLOG.md) #1.
187+
- **Decoupling `EntryMapper` from `org.vaadin.stefan:fullcalendar2`**
188+
— would make `calendar-caldav` truly transitive-free of Vaadin
189+
at the cost of an extra DTO layer. Accept the transitive for v1.
190+
- **Provider-agnostic preset catalogue** — for now `iCloud` ships
191+
hard-coded in `CalDavProviderPreset.DEFAULTS`; Nextcloud /
192+
Radicale / Baïkal entries would be drop-in additions to that
193+
list when the demand surfaces.
194+
- **CSS-as-code variants** — the bundled CSS is fully themable via
195+
Lumo custom properties (`--lumo-*`) and the BEM-ish class names
196+
documented in `calendar-component/README.md`. No Lumo-fork.
197+
198+
## 7. Verification commands
199+
200+
```bash
201+
./mvnw test # 284 tests across the reactor
202+
./mvnw -pl <m> spotbugs:check # 0 findings each module
203+
./mvnw -pl <m> validate # 0 checkstyle violations (add-ons)
204+
./mvnw -DskipTests install # main + sources + javadoc jars
205+
# in calendar-{caldav,component}/target/
206+
```
207+
208+
End-to-end smoke:
209+
210+
```bash
211+
./start-caldav-dev-server.sh # Terminal 1
212+
./start-vaadin-demo.sh # Terminal 2
213+
# http://localhost:8080/, login → /calendar
214+
```

start-caldav-dev-server.sh

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
#!/usr/bin/env bash
2+
# Start the in-process caldav-testbench on port 5232 seeded with a
3+
# "personal" collection. Parks until you hit Ctrl+C.
4+
#
5+
# After Phase 2 of the calendar extraction the launcher class lives
6+
# in the calendar-caldav module under the published add-on namespace
7+
# (com.svenruppert.vaadin.calendar.*), so the exec:java invocation
8+
# needs `-pl calendar-caldav` to find both the test-scope dep
9+
# (caldav-testbench) and the dev launcher class itself.
10+
#
11+
# Companion to README.md → "Running with a CalDAV backend — Option A".
12+
213
set -euo pipefail
314

415
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
516
cd "${ROOT_DIR}"
617

7-
exec ./mvnw exec:java \
18+
exec ./mvnw -pl calendar-caldav exec:java \
819
-Dexec.classpathScope=test \
9-
-Dexec.mainClass=junit.com.svenruppert.flow.calendar.CalDavDevServer \
20+
-Dexec.mainClass=junit.com.svenruppert.vaadin.calendar.CalDavDevServer \
1021
"$@"

start-vaadin-demo.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ set -euo pipefail
2020
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
2121
cd "$SCRIPT_DIR"
2222

23-
exec ./mvnw exec:java \
23+
# After Phase 2 of the calendar extraction the demo lives in the
24+
# `demo/` submodule, so the reactor build needs `-pl demo` to scope
25+
# exec:java to it. The class FQN itself is unchanged.
26+
exec ./mvnw -pl demo exec:java \
2427
-Dexec.classpathScope=test \
2528
-Dexec.mainClass=com.svenruppert.flow.CalDavDemo \
2629
"$@"

0 commit comments

Comments
 (0)