Skip to content

Commit a9d5516

Browse files
committed
Add post:dhorobha-server-local-setup-blog
1 parent bd80ba7 commit a9d5516

1 file changed

Lines changed: 203 additions & 0 deletions

File tree

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
---
2+
title: "Getting the Dhorobha Backend Running Locally: Setup Notes and Pitfalls"
3+
date: 2026-10-15
4+
draft: false
5+
tags: ["Java", "Maven", "Tomcat", "PostgreSQL", "setup", "debugging"]
6+
summary: "How I set up the Dhorobha Java backend on my Mac: the stack, the steps, and six problems I ran into along the way."
7+
---
8+
9+
My first milestone on this work project was getting the **Dhorobha** backend running on my own machine. Dhorobha is a platform for real estate listings (buying and selling) and land-use permit applications. It has three parts: a Java backend, a React website, and a mobile app. Since both the website and the app depend on the backend, the server had to come first.
10+
11+
This post records how I got it running, the problems I hit, and what I learned from each one.
12+
13+
> Note: the project depends on two libraries developed in-house, which I'll call **Framework B** and **Library D** here. Internal code and configuration details are left out.
14+
15+
## The stack
16+
17+
Here's what happens when I open `http://localhost:8080/` in the browser:
18+
19+
{{< mermaid >}}
20+
flowchart LR
21+
A[Browser] -->|HTTP request| B[Tomcat<br/>port 8080]
22+
B --> C[Dhorobha Server<br/>ROOT.war]
23+
C --> D[Framework B<br/>+ Library D]
24+
D -->|reads| E[Config file]
25+
D -->|SQL| F[(PostgreSQL 17<br/>port 5432)]
26+
C -->|HTML / JS / CSS| A
27+
{{< /mermaid >}}
28+
29+
1. **Tomcat** listens on port 8080 and hands the request to the Dhorobha Server app.
30+
2. The server is built on **Framework B**, which depends on **Library D**. On startup, B reads a config file to find the database address and credentials.
31+
3. B connects to **PostgreSQL** and loads the data.
32+
4. The server returns HTML, JS, and CSS, and the browser renders the React frontend.
33+
34+
Every link in this chain has to be in place: Java, Maven, the two internal libraries, PostgreSQL with data, the config file, and Tomcat.
35+
36+
| Tool | Version | Role |
37+
|---|---|---|
38+
| JDK | 21 | Compile and run Java |
39+
| Maven | 3.9.x | Build tool and dependency manager |
40+
| PostgreSQL | 17 | Database |
41+
| Tomcat | 10.1.x | Servlet container that runs the Java web app |
42+
43+
My environment: Intel Mac, Homebrew, bash.
44+
45+
## Setup steps
46+
47+
### 1. Get the code
48+
49+
I forked the repository, cloned my fork with GitHub Desktop (choosing "To contribute to the parent project" so the original repo is set up as `upstream`), and created a working branch.
50+
51+
### 2. Switch to Java 21
52+
53+
The project's `pom.xml` requires Java 21, but my default was 17. I switched only for the current terminal, so other projects keep using 17:
54+
55+
```bash
56+
/usr/libexec/java_home -V # list installed JDKs
57+
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
58+
mvn -v # confirm Maven uses Java 21
59+
```
60+
61+
### 3. Install the internal libraries into the local Maven repository
62+
63+
Framework B and Library D aren't published online, so Maven can't download them. I installed the jars provided by the team into `~/.m2/repository` manually, D first because B depends on it:
64+
65+
```bash
66+
mvn install:install-file -Dfile=<library-d>.jar -DpomFile=<library-d>/pom.xml
67+
mvn install:install-file -Dfile=<framework-b>.jar -DpomFile=<framework-b>/pom.xml
68+
```
69+
70+
Passing `-DpomFile` lets Maven know each library's coordinates and its own dependencies.
71+
72+
### 4. Build
73+
74+
```bash
75+
mvn clean package
76+
```
77+
78+
Success looks like `BUILD SUCCESS` and a `.war` file in `target/`. This step failed for me at first; see Problem 3.
79+
80+
### 5. Prepare the database
81+
82+
```bash
83+
brew services start postgresql@17
84+
psql -d postgres -c "CREATE ROLE \"<DbUser>\" WITH LOGIN PASSWORD '<password>';"
85+
psql -d postgres -c "CREATE DATABASE \"<DbName>\" OWNER \"<DbUser>\";"
86+
pg_restore -U <DbUser> -d <DbName> --no-owner --no-privileges <backup-file>
87+
```
88+
89+
- The names must match the config file exactly. PostgreSQL lowercases unquoted names, so names with capital letters need double quotes (escaped as `\"` inside a bash string).
90+
- `--no-owner --no-privileges` skips the original owners and permissions recorded in the backup, which don't exist on my machine.
91+
92+
### 6. Place the config file
93+
94+
Framework B looks for its config file in a `Data` folder under Tomcat's working directory (`CATALINA_BASE`). I found the exact location by reading the framework's source rather than guessing. To find `CATALINA_BASE`:
95+
96+
```bash
97+
/usr/local/opt/tomcat@10/bin/catalina version
98+
```
99+
100+
Then I created the expected folder and copied the config file into it.
101+
102+
### 7. Deploy as ROOT and start Tomcat
103+
104+
```bash
105+
BASE=/usr/local/Cellar/tomcat@10/10.1.47/libexec
106+
mv "$BASE/webapps/ROOT" "$BASE/webapps/ROOT.tomcat-default" # first time only
107+
cp target/<app>.war "$BASE/webapps/ROOT.war"
108+
JAVA_HOME=$(/usr/libexec/java_home -v 21) "$BASE/bin/catalina.sh" run
109+
```
110+
111+
When the log shows the app started and `Server startup in [...] milliseconds`, the Dhorobha home page loads at `http://localhost:8080/`. 🎉
112+
113+
## Problems I ran into
114+
115+
### Problem 1: PostgreSQL 17 wouldn't start, and `psql` wasn't found
116+
117+
- **Symptom**: `brew services list` showed `postgresql@17 error`, and the terminal said `psql: command not found`.
118+
- **Cause**: two separate issues. PostgreSQL 16 was also installed and already holding port 5432, so 17 couldn't bind to it. And Homebrew's versioned packages like `postgresql@17` are **keg-only**: their commands aren't added to `PATH`, to avoid conflicts between versions.
119+
- **How I found it**: the log (`tail -n 20 /usr/local/var/log/postgresql@17.log`) said `Address already in use`.
120+
- **Fix**:
121+
122+
```bash
123+
brew services stop postgresql@16
124+
brew services start postgresql@17
125+
echo 'export PATH="/usr/local/opt/postgresql@17/bin:$PATH"' >> ~/.bash_profile
126+
source ~/.bash_profile
127+
```
128+
129+
### Problem 2: The build needed Java 21, but I had 17
130+
131+
- **Cause**: multiple JDKs installed; `java -version` shows only the current default.
132+
- **Fix**: `export JAVA_HOME=$(/usr/libexec/java_home -v 21)` (Step 2).
133+
134+
### Problem 3: 100 identical compile errors ⭐
135+
136+
This was the most interesting problem, and the debugging process taught me the most.
137+
138+
- **Symptom**: `mvn clean package` failed with 100 errors, all in the same file, all saying:
139+
140+
```
141+
non-static method getEntityManager(java.lang.String) cannot be referenced from a static context
142+
```
143+
144+
- **How I narrowed it down**:
145+
1. **Look for the pattern.** 100 identical errors in one file usually mean one root cause, not 100 problems.
146+
2. **Read the failing line.** The code called `getEntityManager` statically, through a class name.
147+
3. **Find where the method is defined.** It wasn't in the project. The class extended a base class from Framework B, and its header said it was generated by B and shouldn't be modified. So editing the code wasn't the answer.
148+
4. **Inspect the jar directly.** `javap` shows method signatures inside a jar without source code:
149+
150+
```bash
151+
javap -cp <framework-b>.jar <package>.<BaseClass> | grep getEntityManager
152+
```
153+
154+
In my local jar, the method was **not** `static`.
155+
5. **Check the timeline.** My local jar was installed the last time I set up this environment, almost a year earlier. The project code had since been regenerated with a newer version of Framework B.
156+
157+
- **Cause**: Framework B had been updated (the method became `static`), but the version number stayed the same. Maven saw that version already in my local repository and never fetched it again, so I was compiling against an outdated jar.
158+
- **Fix**: back up `~/.m2/repository`, install the latest jars from the team, and confirm with `javap` that `static` now appears. The build passed.
159+
- **Lesson**: **the same version number doesn't guarantee the same contents.** When a dependency behaves strangely, check what's actually inside the jar.
160+
161+
### Problem 4: The database import "succeeded" but had no tables
162+
163+
- **Symptom**: `psql -f backup.sql` ran without errors, but `\dt` said `Did not find any relations`.
164+
- **Cause**: despite the `.sql` extension, the file was a `pg_dump` **custom-format** (binary) backup, which `psql` can't read.
165+
- **How I found it**: `head -c 5 backup.sql` printed `PGDMP`, the signature of the custom format.
166+
- **Fix**: import with `pg_restore` instead.
167+
- **Lesson**: don't trust file extensions; look at the file's first bytes.
168+
169+
### Problem 5: A blank page with the title "null - null"
170+
171+
- **Symptom**: deployed as `DhorobhaServer.war`, the app at `http://localhost:8080/DhorobhaServer/` showed a blank page.
172+
- **Cause**: the React build references its assets with absolute paths like `/static/js/main.xxx.js`. A leading `/` means "start from the site root," so the browser requested `http://localhost:8080/static/...`, which returned 404 because the app lived under `/DhorobhaServer/`.
173+
- **How I found it**: viewed the page source (`Cmd + Option + U`), then opened the JS URL directly and got a 404.
174+
- **Fix**: rename the war to **`ROOT.war`**. In Tomcat, the war filename becomes the URL path, and `ROOT` means the site root.
175+
176+
### Problem 6: Tomcat ran on Java 25 even though I set `JAVA_HOME`
177+
178+
- **Symptom**: the startup log said `JVM Version: 25`.
179+
- **Cause**: Homebrew's `catalina` wrapper script forces Homebrew's own OpenJDK and ignores `JAVA_HOME`.
180+
- **Fix**: call Tomcat's own script, `libexec/bin/catalina.sh`, with `JAVA_HOME` set on the same line (Step 7).
181+
182+
### Bonus: hundreds of changed files after every build
183+
184+
After `mvn clean package`, GitHub Desktop listed hundreds of changes under `target/`, Maven's build output folder. Some build output is tracked in the repository, so every build modifies it. I never commit anything under `target/`, and I discard those changes before pulling. For newly generated files, a local-only ignore rule helps without touching the shared `.gitignore`:
185+
186+
```bash
187+
echo "target/" >> .git/info/exclude
188+
```
189+
190+
`.git/info/exclude` works like `.gitignore` but only on my machine. Note that neither one affects files Git is already tracking.
191+
192+
## Takeaways
193+
194+
Looking back, the same approach solved every problem:
195+
196+
1. **Read the error message carefully.** `Address already in use` and `command not found` already say what's wrong.
197+
2. **Look for patterns.** Many identical errors usually share one root cause.
198+
3. **Form a hypothesis, then verify it with evidence**: `javap` for method signatures, `ls -la` for file dates, `head` for file contents, logs for runtime behavior.
199+
4. **Change one thing at a time**: build first, then the database, then deployment.
200+
5. **The code is the best documentation.** When no one could tell me where the config file belonged, the framework's source could.
201+
6. **Back up before changing anything.** I backed up the Maven repository before replacing jars, and renamed Tomcat's default `ROOT` folder instead of deleting it.
202+
203+
Next up: starting on actual feature work. Questions or suggestions are welcome on [GitHub](https://github.com/sagitdev).

0 commit comments

Comments
 (0)