Developing with PostgreSQL as the Wikantik User Database

This document provides a complete, step-by-step guide for configuring PostgreSQL as the backing database for Wikantik's user and group management system. It covers development setup, testing, validation, and production deployment.

Table of Contents

1. [Overview](#1-overview)

2. [Prerequisites](#2-prerequisites)

3. [PostgreSQL Server Setup](#3-postgresql-server-setup)

4. [Database Schema Creation](#4-database-schema-creation)

5. [JDBC Driver Installation](#5-jdbc-driver-installation)

6. [Tomcat JNDI Configuration](#6-tomcat-jndi-configuration)

7. [Wikantik Configuration](#7-wikantik-configuration)

8. [Testing the Configuration](#8-testing-the-configuration)

9. [Validation Procedures](#9-validation-procedures)

10. [Production Deployment](#10-production-deployment)

11. [Troubleshooting](#11-troubleshooting)

12. [Appendix: Password Hashing](#appendix-password-hashing)

---

1. Overview

What This Document Covers

Wikantik can store user profiles and group memberships in either:

- **XML files** (default) - Simple, file-based storage suitable for small installations

- **Relational database** - Scalable, performant storage for production environments

This guide focuses on using **PostgreSQL** as the relational database backend, which is recommended for:

- Production environments with multiple users

- High-availability deployments

- Integration with existing PostgreSQL infrastructure

- Enhanced security through database-level access controls

Architecture

```

┌─────────────────────────────────────────────────────────────────┐

│ Wikantik │

├─────────────────────────────────────────────────────────────────┤

│ JDBCUserDatabase JDBCGroupDatabase │

│ │ │ │

│ └──────────┬───────────────────┘ │

│ │ │

│ JNDI Lookup │

│ (java:comp/env/jdbc/WikiDatabase) │

└────────────────────┼────────────────────────────────────────────┘

┌────────────────────┼────────────────────────────────────────────┐

│ ▼ │

│ Tomcat DataSource Pool │

│ (jdbc/WikiDatabase) │

└────────────────────┼────────────────────────────────────────────┘

┌────────────────────┼────────────────────────────────────────────┐

│ ▼ │

│ PostgreSQL Server │

│ ┌─────────┬─────────┬────────────────┬──────────────┐ │

│ │ users │ roles │ groups │ group_members│ │

│ └─────────┴─────────┴────────────────┴──────────────┘ │

└─────────────────────────────────────────────────────────────────┘

```

Key Implementation Details

From the source code (`JDBCUserDatabase.java` and `JDBCGroupDatabase.java`):

- **JNDI Lookup**: Wikantik uses JNDI to look up DataSources, not direct JDBC connections

- **Password Hashing**: Passwords are stored using salted SHA-1 (`{SSHA}`) or SHA-256 (`{SHA-256}`) format

- **Transaction Support**: Automatic detection and use of database transactions

- **Prepared Statements**: All SQL uses prepared statements (immune to SQL injection)

- **User Attributes**: Custom attributes are serialized as Base64-encoded Java objects

---

2. Prerequisites

Software Requirements

| Component | Minimum Version | Recommended |

|-----------|----------------|-------------|

| PostgreSQL | 12.0 | 15.0+ |

| Java JDK | 11 | 17+ |

| Apache Tomcat | 9.0 | 11.0 |

| PostgreSQL JDBC Driver | 42.2.x | 42.7.x |

Required Knowledge

- Basic PostgreSQL administration (creating databases, users, grants)

- Tomcat configuration (context.xml, JNDI resources)

- Wikantik properties configuration

---

3. PostgreSQL Server Setup

3.1 Install PostgreSQL

**Ubuntu/Debian:**

```bash

sudo apt update

sudo apt install postgresql postgresql-contrib

sudo systemctl enable postgresql

sudo systemctl start postgresql

```

**RHEL/CentOS/Fedora:**

```bash

sudo dnf install postgresql-server postgresql-contrib

sudo postgresql-setup --initdb

sudo systemctl enable postgresql

sudo systemctl start postgresql

```

**macOS (Homebrew):**

```bash

brew install postgresql@15

brew services start postgresql@15

```

3.2 Create the Wikantik Database

Connect to PostgreSQL as the superuser:

```bash

sudo -u postgres psql

```

Create the database:

```sql

-- Create the database

CREATE DATABASE wikantik

WITH

ENCODING = 'UTF8'

LC_COLLATE = 'en_US.UTF-8'

LC_CTYPE = 'en_US.UTF-8'

TEMPLATE = template0;

-- Verify creation

\l wikantik

```

3.3 Create the Application User

Create a dedicated database user for Wikantik:

```sql

-- Create the user with a strong password

CREATE USER jspwiki WITH

ENCRYPTED PASSWORD 'your_secure_password_here'

NOSUPERUSER

NOCREATEDB

NOCREATEROLE;

-- Grant connection privileges

GRANT CONNECT ON DATABASE wikantik TO jspwiki;

-- Connect to the wikantik database

\c wikantik

-- Grant schema usage and creation privileges

-- NOTE: PostgreSQL 15+ restricts CREATE on public schema by default

GRANT USAGE ON SCHEMA public TO jspwiki;

GRANT CREATE ON SCHEMA public TO jspwiki;

```

**Important (PostgreSQL 15+):** Starting with PostgreSQL 15, the `CREATE` privilege on the `public` schema is no longer granted to all users by default. You must explicitly grant it as shown above, or create the tables as a superuser and only grant DML permissions to the application user.

**Security Note:** In production, use a strong, randomly generated password. Consider using a password manager or secrets management system.

3.4 Configure PostgreSQL Authentication

Edit `pg_hba.conf` to allow the jspwiki user to connect:

```bash

Find the pg_hba.conf location

sudo -u postgres psql -c "SHOW hba_file;"

```

Add the following line (adjust for your network):

```

TYPE DATABASE USER ADDRESS METHOD

host wikantik jspwiki 127.0.0.1/32 scram-sha-256

host wikantik jspwiki ::1/128 scram-sha-256

```

Reload PostgreSQL configuration:

```bash

sudo systemctl reload postgresql

```

---

4. Database Schema Creation

4.1 Complete PostgreSQL Schema

**Important:** The schema creation script must be run as a PostgreSQL **superuser** (e.g., `postgres`) because it:

- Creates/drops the `jspwiki` application user

- Grants permissions on tables

Connect to the wikantik database as the superuser:

```bash

Option 1: Run the DDL file directly

sudo -u postgres psql -d wikantik -f wikantik-war/src/main/config/db/postgresql.ddl

Option 2: Connect interactively as superuser

sudo -u postgres psql -d wikantik

```

Execute the following DDL:

```sql

-- ============================================================================

-- Wikantik PostgreSQL Schema

-- Based on: wikantik-war/src/main/config/db/postgresql.ddl

-- Compatible with: JDBCUserDatabase.java and JDBCGroupDatabase.java

-- ============================================================================

-- Drop existing tables if recreating (BE CAREFUL IN PRODUCTION!)

-- DROP TABLE IF EXISTS group_members CASCADE;

-- DROP TABLE IF EXISTS groups CASCADE;

-- DROP TABLE IF EXISTS roles CASCADE;

-- DROP TABLE IF EXISTS users CASCADE;

-- ============================================================================

-- Users Table

-- Stores user profiles for authentication and personalization

-- ============================================================================

CREATE TABLE users (

-- Unique identifier for the user (auto-generated by Wikantik)

uid VARCHAR(100),

-- User's email address (used for password recovery, notifications)

email VARCHAR(100),

-- User's full display name

full_name VARCHAR(100),

-- Login name (username) - PRIMARY KEY, must be unique

login_name VARCHAR(100) NOT NULL,

-- Hashed password in {SSHA} or {SHA-256} format

password VARCHAR(100),

-- Wiki-formatted name (typically CamelCase of full_name)

wiki_name VARCHAR(100),

-- Profile creation timestamp

created TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,

-- Last modification timestamp

modified TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,

-- Account lock expiry (NULL = not locked)

lock_expiry TIMESTAMP WITH TIME ZONE,

-- Serialized user attributes (Base64-encoded Java Map)

-- Used for: OAuth metadata, custom profile fields, preferences

attributes TEXT,

PRIMARY KEY (login_name)

);

-- Create indexes for common lookup patterns

CREATE INDEX idx_users_email ON users(email);

CREATE INDEX idx_users_wiki_name ON users(wiki_name);

CREATE INDEX idx_users_uid ON users(uid);

-- ============================================================================

-- Roles Table

-- Stores container-managed authentication roles

-- Each user can have multiple roles (one row per role)

-- ============================================================================

CREATE TABLE roles (

-- References users.login_name

login_name VARCHAR(100) NOT NULL,

-- Role name (e.g., 'Authenticated', 'Admin', 'Editor')

role VARCHAR(100) NOT NULL,

-- Composite key to prevent duplicate role assignments

PRIMARY KEY (login_name, role)

);

-- Create index for role lookups

CREATE INDEX idx_roles_login_name ON roles(login_name);

-- ============================================================================

-- Groups Table

-- Stores wiki group definitions

-- ============================================================================

CREATE TABLE groups (

-- Group name - PRIMARY KEY, must be unique

name VARCHAR(100) NOT NULL,

-- User who created the group

creator VARCHAR(100),

-- Group creation timestamp

created TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,

-- User who last modified the group

modifier VARCHAR(100),

-- Last modification timestamp

modified TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,

PRIMARY KEY (name)

);

-- ============================================================================

-- Group Members Table

-- Stores group membership (many-to-many relationship)

-- ============================================================================

CREATE TABLE group_members (

-- References groups.name

name VARCHAR(100) NOT NULL,

-- Member name (wiki name of the user)

member VARCHAR(100) NOT NULL,

-- Composite primary key

PRIMARY KEY (name, member)

);

-- Create index for member lookups

CREATE INDEX idx_group_members_member ON group_members(member);

```

4.2 Grant Table Permissions

After creating the tables, grant the necessary permissions:

```sql

-- Grant DML permissions on all tables

GRANT SELECT, INSERT, UPDATE, DELETE ON users TO jspwiki;

GRANT SELECT, INSERT, UPDATE, DELETE ON roles TO jspwiki;

GRANT SELECT, INSERT, UPDATE, DELETE ON groups TO jspwiki;

GRANT SELECT, INSERT, UPDATE, DELETE ON group_members TO jspwiki;

```

4.3 Create Initial Admin User

Create an initial administrator account:

```sql

-- Insert the admin user

-- Password: 'admin' hashed with {SSHA}

-- You should change this password immediately after first login!

INSERT INTO users (

uid,

email,

full_name,

login_name,

password,

wiki_name,

created,

modified

) VALUES (

'-6852820166199419346',

'admin@localhost',

'Administrator',

'admin',

'{SSHA}6YNKYMwXICUf5pMvYUZumgbFCxZMT2njtUQtJw==',

'Administrator',

CURRENT_TIMESTAMP,

CURRENT_TIMESTAMP

);

-- Assign the Admin role

INSERT INTO roles (login_name, role) VALUES ('admin', 'Admin');

INSERT INTO roles (login_name, role) VALUES ('admin', 'Authenticated');

-- Create the Admin group

INSERT INTO groups (name, created, modified)

VALUES ('Admin', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);

-- Add Administrator to the Admin group

INSERT INTO group_members (name, member) VALUES ('Admin', 'Administrator');

```

4.4 Verify Schema

Verify the tables were created correctly:

```sql

-- List all tables

\dt

-- Describe the users table

\d users

-- Verify the admin user

SELECT login_name, full_name, email, wiki_name FROM users;

-- Verify roles

SELECT * FROM roles;

-- Verify groups

SELECT * FROM groups;

SELECT * FROM group_members;

```

---

5. JDBC Driver Installation

5.1 Download the PostgreSQL JDBC Driver

Download the PostgreSQL JDBC driver from the official website:

- https://jdbc.postgresql.org/download/

Choose the JDBC 4.2 driver (for Java 8+) or JDBC 4.3 driver (for Java 11+).

Example download using curl:

```bash

Download PostgreSQL JDBC Driver 42.7.4 (check for latest version)

curl -L -o postgresql-42.7.4.jar \

https://jdbc.postgresql.org/download/postgresql-42.7.4.jar

```

5.2 Install in Tomcat

Copy the JDBC driver to Tomcat's lib directory:

```bash

For the project's bundled Tomcat

cp postgresql-42.7.4.jar /home/jakefear/source/jspwiki/tomcat/tomcat-11/lib/

For a system Tomcat installation

cp postgresql-42.7.4.jar $CATALINA_HOME/lib/

```

**Important:** Restart Tomcat after adding the driver.

---

6. Tomcat JNDI Configuration

6.1 Configure DataSource Resources

Edit Tomcat's `context.xml` or create an application-specific context file.

**Option A: Global Context (conf/context.xml)**

Edit `$CATALINA_HOME/conf/context.xml`:

```xml

<?xml version="1.0" encoding="UTF-8"?>

<Context>

<!-- Default session configuration -->

<WatchedResource>WEB-INF/web.xml</WatchedResource>

<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>

<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

<!-- Wikantik Wiki Database DataSource (shared by all subsystems) -->

<Resource

name="jdbc/WikiDatabase"

auth="Container"

type="javax.sql.DataSource"

driverClassName="org.postgresql.Driver"

url="jdbc:postgresql://localhost:5432/wikantik"

username="jspwiki"

password="your_secure_password_here"

<!-- Connection Pool Settings -->

maxTotal="30"

maxIdle="10"

minIdle="5"

maxWaitMillis="10000"

<!-- Connection Validation -->

validationQuery="SELECT 1"

validationQueryTimeout="5"

testOnBorrow="true"

testOnReturn="false"

testWhileIdle="true"

timeBetweenEvictionRunsMillis="30000"

minEvictableIdleTimeMillis="60000"

<!-- Connection Settings -->

removeAbandonedOnBorrow="true"

removeAbandonedOnMaintenance="true"

removeAbandonedTimeout="300"

logAbandoned="true"

/>

</Context>

```

**Option B: Application-Specific Context**

Create `$CATALINA_HOME/conf/Catalina/localhost/Wikantik.xml`:

```xml

<?xml version="1.0" encoding="UTF-8"?>

<Context docBase="/path/to/Wikantik.war" path="/Wikantik">

<Resource

name="jdbc/WikiDatabase"

auth="Container"

type="javax.sql.DataSource"

driverClassName="org.postgresql.Driver"

url="jdbc:postgresql://localhost:5432/wikantik"

username="jspwiki"

password="your_secure_password_here"

maxTotal="30"

maxIdle="10"

minIdle="5"

maxWaitMillis="10000"

validationQuery="SELECT 1"

testOnBorrow="true"

/>

</Context>

```

6.2 Connection Pool Settings Explained

| Setting | Description | Recommended Value |

|---------|-------------|-------------------|

| `maxTotal` | Maximum active connections | 50-100 |

| `maxIdle` | Maximum idle connections | 10-20 |

| `minIdle` | Minimum idle connections | 5-10 |

| `maxWaitMillis` | Max wait time for connection | 10000 (10 sec) |

| `validationQuery` | Query to validate connections | `SELECT 1` |

| `testOnBorrow` | Validate before using | `true` |

| `timeBetweenEvictionRunsMillis` | Eviction check interval | 30000 (30 sec) |

| `minEvictableIdleTimeMillis` | Min idle time before eviction | 60000 (1 min) |

---

7. Wikantik Configuration

7.1 Configure wikantik-custom.properties

Create or edit `WEB-INF/wikantik-custom.properties`:

```properties

============================================================================

Wikantik JDBC User/Group Database Configuration

============================================================================

Enable JDBC User Database (instead of default XML)

jspwiki.userdatabase = com.wikantik.auth.user.JDBCUserDatabase

Enable JDBC Group Database (instead of default XML)

jspwiki.groupdatabase = com.wikantik.auth.authorize.JDBCGroupDatabase

============================================================================

JNDI DataSource Name

Must match the Resource name in Tomcat's context.xml

============================================================================

wikantik.datasource = jdbc/WikiDatabase

```

7.2 Verify web.xml Resource References (Optional)

If using resource-ref declarations, ensure `WEB-INF/web.xml` includes:

```xml

<resource-ref>

<description>Wiki Database Connection</description>

<res-ref-name>jdbc/WikiDatabase</res-ref-name>

<res-type>javax.sql.DataSource</res-type>

<res-auth>Container</res-auth>

</resource-ref>

```

---

8. Testing the Configuration

8.1 Development Build and Deploy

Build Wikantik and deploy to the development Tomcat:

```bash

cd /home/jakefear/source/jspwiki

Build the project (skip tests for quick iteration)

mvn clean install -Dmaven.test.skip

Copy the WAR to Tomcat

cp wikantik-war/target/Wikantik.war tomcat/tomcat-11/webapps/

Start Tomcat

./tomcat/tomcat-11/bin/startup.sh

Tail the logs

tail -f tomcat/tomcat-11/logs/catalina.out

```

8.2 Verify Database Connectivity

Check the logs for successful JDBC initialization:

```bash

grep -E "(JDBCUserDatabase|JDBCGroupDatabase)" tomcat/tomcat-11/logs/catalina.out

```

Expected output:

```

JDBCUserDatabase initialized from JNDI DataSource: jdbc/WikiDatabase

JDBCUserDatabase supports transactions. Good; we will use them.

JDBCGroupDatabase initialized from JNDI DataSource: jdbc/WikiDatabase

JDBCGroupDatabase supports transactions. Good; we will use them.

```

8.3 Test Login

1. Open http://localhost:8080/Wikantik

2. Click "Login"

3. Enter credentials:

- Username: `admin`

- Password: `admin` (or whatever you set)

4. Verify successful login

8.4 Test User Registration

1. Log out

2. Click "Register"

3. Fill in the registration form

4. Complete registration

5. Verify the new user appears in the database:

```sql

SELECT login_name, full_name, email, created FROM users ORDER BY created DESC LIMIT 5;

```

8.5 Test Group Management

1. Log in as admin

2. Go to Group Management (Admin > Group Management)

3. Create a new group

4. Add members

5. Verify in database:

```sql

SELECT g.name, g.creator, gm.member

FROM groups g

LEFT JOIN group_members gm ON g.name = gm.name

ORDER BY g.name;

```

---

9. Validation Procedures

9.1 Database Connection Validation

Create a simple test script to validate connectivity:

```bash

!/bin/bash

test_postgres_connection.sh

echo "Testing PostgreSQL connection..."

psql -h localhost -U jspwiki -d wikantik -c "SELECT 1 as connection_test;"

echo ""

echo "Checking table structure..."

psql -h localhost -U jspwiki -d wikantik -c "\dt"

echo ""

echo "Checking user count..."

psql -h localhost -U jspwiki -d wikantik -c "SELECT COUNT(*) as user_count FROM users;"

echo ""

echo "Checking group count..."

psql -h localhost -U jspwiki -d wikantik -c "SELECT COUNT(*) as group_count FROM groups;"

```

9.2 Application Health Check

Create a SQL script to validate the database state:

```sql

-- health_check.sql

-- Run this periodically to validate database health

-- Check table existence

SELECT

table_name,

CASE WHEN table_name IS NOT NULL THEN 'EXISTS' ELSE 'MISSING' END as status

FROM information_schema.tables

WHERE table_schema = 'public'

AND table_name IN ('users', 'roles', 'groups', 'group_members');

-- Check for orphaned roles (roles without users)

SELECT r.login_name, r.role

FROM roles r

LEFT JOIN users u ON r.login_name = u.login_name

WHERE u.login_name IS NULL;

-- Check for orphaned group members

SELECT gm.name, gm.member

FROM group_members gm

LEFT JOIN groups g ON gm.name = g.name

WHERE g.name IS NULL;

-- Check for users without roles

SELECT u.login_name

FROM users u

LEFT JOIN roles r ON u.login_name = r.login_name

WHERE r.login_name IS NULL;

-- Recent user activity

SELECT login_name, modified

FROM users

WHERE modified > CURRENT_TIMESTAMP - INTERVAL '7 days'

ORDER BY modified DESC;

```

9.3 Unit Test Execution

Run the JDBC-related unit tests:

```bash

cd /home/jakefear/source/jspwiki

Run JDBCUserDatabase tests

mvn test -Dtest=JDBCUserDatabaseTest -pl wikantik-main

Run JDBCGroupDatabase tests

mvn test -Dtest=JDBCGroupDatabaseTest -pl wikantik-main

```

---

10. Production Deployment

10.1 Security Hardening

**Database Security:**

```sql

-- Use a strong, unique password

ALTER USER jspwiki WITH PASSWORD 'use_a_very_strong_password_here_min_32_chars';

-- Limit connection privileges

REVOKE ALL ON DATABASE wikantik FROM PUBLIC;

GRANT CONNECT ON DATABASE wikantik TO jspwiki;

-- Limit schema privileges

REVOKE ALL ON SCHEMA public FROM PUBLIC;

GRANT USAGE ON SCHEMA public TO jspwiki;

-- Only grant required table permissions

REVOKE ALL ON ALL TABLES IN SCHEMA public FROM PUBLIC;

GRANT SELECT, INSERT, UPDATE, DELETE ON users TO jspwiki;

GRANT SELECT, INSERT, UPDATE, DELETE ON roles TO jspwiki;

GRANT SELECT, INSERT, UPDATE, DELETE ON groups TO jspwiki;

GRANT SELECT, INSERT, UPDATE, DELETE ON group_members TO jspwiki;

```

**Network Security:**

```bash

pg_hba.conf - Production settings

Only allow connections from application server

host wikantik jspwiki 10.0.0.5/32 scram-sha-256

Or use SSL

hostssl wikantik jspwiki 10.0.0.5/32 scram-sha-256 clientcert=verify-ca

```

**SSL/TLS Configuration:**

Update the JDBC URL in context.xml for SSL:

```xml

url="jdbc:postgresql://db-server:5432/wikantik?ssl=true&amp;sslmode=verify-full&amp;sslrootcert=/path/to/ca.crt"

```

10.2 Connection Pool Tuning

For production workloads, tune the connection pool:

```xml

<Resource

name="jdbc/WikiDatabase"

auth="Container"

type="javax.sql.DataSource"

driverClassName="org.postgresql.Driver"

url="jdbc:postgresql://db-server:5432/wikantik"

username="jspwiki"

password="production_password"

<!-- Production Pool Settings -->

maxTotal="100"

maxIdle="20"

minIdle="10"

maxWaitMillis="30000"

<!-- Connection Validation -->

validationQuery="SELECT 1"

validationQueryTimeout="5"

testOnBorrow="true"

testOnReturn="false"

testWhileIdle="true"

timeBetweenEvictionRunsMillis="30000"

minEvictableIdleTimeMillis="60000"

numTestsPerEvictionRun="5"

<!-- Abandoned Connection Handling -->

removeAbandonedOnBorrow="true"

removeAbandonedOnMaintenance="true"

removeAbandonedTimeout="300"

logAbandoned="true"

<!-- Performance Settings -->

defaultAutoCommit="false"

defaultTransactionIsolation="READ_COMMITTED"

poolPreparedStatements="true"

maxOpenPreparedStatements="100"

<!-- Connection Factory -->

factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"

jmxEnabled="true"

/>

```

10.3 Backup and Recovery

**Daily Backup Script:**

```bash

!/bin/bash

backup_wikantik_db.sh

BACKUP_DIR="/var/backups/postgresql"

DATE=$(date +%Y%m%d_%H%M%S)

BACKUP_FILE="${BACKUP_DIR}/wikantik_${DATE}.sql.gz"

Create backup directory if needed

mkdir -p "${BACKUP_DIR}"

Dump and compress

pg_dump -h localhost -U jspwiki -d wikantik | gzip > "${BACKUP_FILE}"

Keep only last 30 days of backups

find "${BACKUP_DIR}" -name "wikantik_*.sql.gz" -mtime +30 -delete

echo "Backup completed: ${BACKUP_FILE}"

```

**Restore Procedure:**

```bash

Stop Wikantik/Tomcat first

systemctl stop tomcat

Restore from backup

gunzip -c /var/backups/postgresql/wikantik_20250101_120000.sql.gz | psql -h localhost -U jspwiki -d wikantik

Start Wikantik/Tomcat

systemctl start tomcat

```

10.4 Monitoring

**PostgreSQL Queries for Monitoring:**

```sql

-- Active connections

SELECT count(*) FROM pg_stat_activity WHERE datname = 'wikantik';

-- Connection state breakdown

SELECT state, count(*)

FROM pg_stat_activity

WHERE datname = 'wikantik'

GROUP BY state;

-- Table sizes

SELECT

relname as table_name,

pg_size_pretty(pg_total_relation_size(relid)) as total_size,

pg_size_pretty(pg_relation_size(relid)) as data_size

FROM pg_catalog.pg_statio_user_tables

WHERE schemaname = 'public'

ORDER BY pg_total_relation_size(relid) DESC;

-- Index usage

SELECT

indexrelname as index_name,

idx_scan as index_scans,

idx_tup_read as tuples_read,

idx_tup_fetch as tuples_fetched

FROM pg_stat_user_indexes

WHERE schemaname = 'public';

```

---

11. Troubleshooting

11.1 Common Errors

**Error: DataSource not found**

```

javax.naming.NameNotFoundException: Name [jdbc/WikiDatabase] is not bound

```

**Solution:**

- Verify the Resource is defined in context.xml

- Check the resource name matches exactly

- Restart Tomcat completely

**Error: Driver not found**

```

java.sql.SQLException: No suitable driver found for jdbc:postgresql://

```

**Solution:**

- Ensure postgresql-X.X.X.jar is in $CATALINA_HOME/lib/

- Restart Tomcat after adding the driver

**Error: Authentication failed**

```

FATAL: password authentication failed for user "jspwiki"

```

**Solution:**

- Verify username/password in context.xml

- Check pg_hba.conf allows the connection

- Verify the user exists: `\du jspwiki`

**Error: Connection refused**

```

java.net.ConnectException: Connection refused

```

**Solution:**

- Verify PostgreSQL is running: `systemctl status postgresql`

- Check PostgreSQL is listening: `ss -tlnp | grep 5432`

- Verify postgresql.conf has `listen_addresses = '*'`

11.2 Debug Logging

Enable JDBC debug logging in `log4j2.xml`:

```xml

<Logger name="com.wikantik.auth.user.JDBCUserDatabase" level="DEBUG"/>

<Logger name="com.wikantik.auth.authorize.JDBCGroupDatabase" level="DEBUG"/>

<Logger name="org.apache.tomcat.jdbc.pool" level="DEBUG"/>

```

11.3 Database Diagnostics

```sql

-- Check for locks

SELECT

pg_stat_activity.pid,

pg_class.relname,

pg_locks.transactionid,

pg_locks.mode,

pg_locks.granted

FROM pg_locks

JOIN pg_class ON pg_locks.relation = pg_class.oid

JOIN pg_stat_activity ON pg_locks.pid = pg_stat_activity.pid

WHERE pg_stat_activity.datname = 'wikantik';

-- Check for long-running queries

SELECT

pid,

now() - pg_stat_activity.query_start AS duration,

query,

state

FROM pg_stat_activity

WHERE datname = 'wikantik'

AND state != 'idle'

AND now() - pg_stat_activity.query_start > interval '5 seconds';

```

---

Appendix: Password Hashing

A.1 Password Format

Wikantik stores passwords using RFC 2307-compliant salted hashing:

- **{SSHA}**: Salted SHA-1 (legacy, still supported)

- **{SHA-256}**: Salted SHA-256 (recommended)

Format: `{ALGORITHM}Base64(hash + salt)`

A.2 Generating Password Hashes

Use the `CryptoUtil` command-line tool:

```bash

cd /home/jakefear/source/jspwiki

Build the project first

mvn clean install -Dmaven.test.skip

Generate a SHA-256 hash (recommended)

java -cp wikantik-util/target/classes com.wikantik.util.CryptoUtil --hash "mypassword" "{SHA-256}"

Generate a SSHA hash (legacy)

java -cp wikantik-util/target/classes com.wikantik.util.CryptoUtil --hash "mypassword" "{SSHA}"

Verify a password against a hash

java -cp wikantik-util/target/classes com.wikantik.util.CryptoUtil --verify "mypassword" "{SHA-256}xyz123..."

```

A.3 Updating a User's Password via SQL

```sql

-- Generate a new hash using CryptoUtil first, then:

UPDATE users

SET password = '{SHA-256}your_generated_hash_here',

modified = CURRENT_TIMESTAMP

WHERE login_name = 'username';

```

---

Document History

| Version | Date | Author | Changes |

|---------|------|--------|---------|

| 1.0 | 2025-11-30 | Claude | Initial comprehensive PostgreSQL setup guide |

| 1.1 | 2026-04-17 | Claude | Repaired heading/list markup damage; fixed ToC anchors; corrected DB/user naming to wikantik/jspwiki |

---

References

- [Wikantik Source: JDBCUserDatabase.java](../wikantik-main/src/main/java/com/wikantik/auth/user/JDBCUserDatabase.java)

- [Wikantik Source: JDBCGroupDatabase.java](../wikantik-main/src/main/java/com/wikantik/auth/authorize/JDBCGroupDatabase.java)

- [PostgreSQL JDBC Driver Documentation](https://jdbc.postgresql.org/documentation/)

- [Apache Tomcat JNDI Resources](https://tomcat.apache.org/tomcat-11.0-doc/jndi-resources-howto.html)

- [PostgreSQL Documentation](https://www.postgresql.org/docs/)