Agent Skills: JWebMP Client

Client SPI library for JWebMP — defines AJAX pipeline contracts (AjaxCall/AjaxResponse), page contracts (IPage/IPageConfigurator), component model interfaces, and interceptor SPIs. Use when working with JWebMP client interfaces, AJAX interception, page configuration SPIs, component model interfaces, or extending JWebMP with custom interceptors and configurators.

UncategorizedID: GuicedEE/ai-rules/jwebmp-client

Install this agent skill to your local

pnpm dlx add-skill https://github.com/GuicedEE/ai-rules/tree/HEAD/skills/.system/jwebmp-client

Skill Files

Browse the full folder contents for jwebmp-client.

Download Skill

Loading file tree…

skills/.system/jwebmp-client/SKILL.md

Skill Metadata

Name
jwebmp-client
Description
"Implement JWebMP AJAX/page contracts, component interfaces, interceptors, and page configurator SPIs."

JWebMP Client

Client SPI library defining contracts, AJAX pipeline, and component model interfaces for JWebMP.

Core Purpose

Provides compile-time contracts and runtime SPI discovery for JWebMP ecosystem without circular dependencies. All JWebMP modules program against these interfaces.

AJAX Pipeline

AjaxCall & AjaxResponse

Core DTO objects for browser ↔ server communication:

// AjaxCall - Incoming request (CallScope-scoped)
public class AjaxCall<J extends AjaxCall<J>> {
    Map<String, String> getParameters();
    String getEventType();
    String getComponentId();
    HeadersDTO getHeaders();
}

// AjaxResponse - Outgoing response
public class AjaxResponse<J extends AjaxResponse<J>> {
    J addComponent(IComponentHierarchyBase<?, ?> component);
    J addReaction(AjaxResponseReaction<?> reaction);
    Map<String, Object> getDataReturns();
}

Response Components

// DOM update descriptor
public class AjaxComponentUpdates {
    AjaxComponentInsertType insertType;  // replace, append, prepend, etc.
    String htmlContent;
    String componentId;
}

// Client-side reaction
public class AjaxResponseReaction<J> {
    ReactionType reactionType;  // redirect, dialog, etc.
    Object value;
}

Interceptor SPIs

Three-Level Interception Chain

// 1. Site-level (first page load)
public interface SiteCallIntercepter<J extends SiteCallIntercepter<J>>
        extends IDefaultService<J> {
    void intercept(AjaxCall<?> call, AjaxResponse<?> response);
}

// 2. AJAX event calls
public interface AjaxCallIntercepter<J extends AjaxCallIntercepter<J>>
        extends SiteCallIntercepter<J> {
    // Same intercept() method
}

// 3. Startup data calls
public interface DataCallIntercepter<J extends DataCallIntercepter<J>>
        extends SiteCallIntercepter<J> {
    // Same intercept() method
}

Interceptor Example

public class AuditInterceptor implements AjaxCallIntercepter<AuditInterceptor> {
    @Override
    public void intercept(AjaxCall<?> call, AjaxResponse<?> response) {
        log.info("AJAX event: {}", call.getEventType());
    }

    @Override
    public Integer sortOrder() {
        return 10;  // Lower = earlier
    }
}

Register via module-info.java:

provides com.jwebmp.interception.services.AjaxCallIntercepter
    with my.app.AuditInterceptor;

Page Contracts

IPage Interface

public interface IPage<J extends IPage<J>> {
    IBody<?> getBody();
    IHead<?> getHead();
    PageOptions getOptions();
    J addJavaScriptReference(JavascriptReference ref);
    J addCssReference(CSSReference ref);
}

IPageConfigurator SPI

public interface IPageConfigurator<J extends IPageConfigurator<J>>
        extends IDefaultService<J>, IServiceEnablement<J> {
    IPage<?> configure(IPage<?> page);
    Integer sortOrder();  // Determines execution order
}

Example Configurator

public class AnalyticsConfigurator implements IPageConfigurator<AnalyticsConfigurator> {
    @Override
    public IPage<?> configure(IPage<?> page) {
        page.addJavaScriptReference(
            new JavascriptReference("analytics", 1.0, "analytics.js")
        );
        return page;
    }

    @Override
    public Integer sortOrder() {
        return 100;
    }
}

Component Model Interfaces

11-layer interface hierarchy (CRTP):

IComponentBase                      // ID, name, properties, JSON serialization
 └─ IComponentHierarchyBase        // parent/child tree, CSS/JS references
     └─ IComponentHTMLBase         // tag rendering, text, raw HTML
         └─ IComponentHTMLAttributeBase  // HTML attributes (typed enums)
             └─ IComponentHTMLOptionsBase   // JavaScript options (JavaScriptPart)
                 └─ IComponentStyleBase        // inline CSS via CSS builder
                     └─ IComponentThemeBase        // theme support
                         └─ IComponentDataBindingBase  // data-bind hooks
                             └─ IComponentDependencyBase   // CSS/JS dependency refs
                                 └─ IComponentFeatureBase     // Feature attachment
                                     └─ IComponentEventBase      // Event attachment

Lifecycle SPIs

Databind / Render Hooks

| SPI | When It Fires | |---|---| | IOnDataBind | Component's data-bind is processed | | IOnDataBindCloak | Cloaked data-bind components | | IOnComponentAdded | Child added to component | | IOnComponentConfigured | After component configuration | | IOnComponentHtmlRender | During component HTML rendering | | IAfterRenderComplete | After full render completes | | IClientVariableWatcher | Client-side variable changes |

Render-Ordering SPIs

| SPI | Purpose | |---|---| | RenderBeforeLinks | Before CSS <link> tags | | RenderAfterLinks | After CSS <link> tags | | RenderBeforeScripts | Before <script> tags | | RenderAfterScripts | After <script> tags | | RenderBeforeDynamicScripts | Before dynamic/inline scripts | | RenderAfterDynamicScripts | After dynamic/inline scripts |

Example Lifecycle Hook

public class ComponentLogger implements IOnComponentAdded<ComponentLogger> {
    @Override
    public void onComponentAdded(IComponentHierarchyBase<?, ?> parent,
                                 IComponentHierarchyBase<?, ?> added) {
        log.info("Added {} to {}", added.getID(), parent.getID());
    }
}

JavaScript Options

JavaScriptPart Base Class

All component options extend JavaScriptPart for JSON serialization:

public class MyComponentOptions extends JavaScriptPart<MyComponentOptions> {
    private String color;
    private Integer size;

    public MyComponentOptions setColor(String color) {
        this.color = color;
        return this;
    }

    public MyComponentOptions setSize(Integer size) {
        this.size = size;
        return this;
    }
}

Automatic Jackson serialization to JSON.

References (CSS/JS)

CSSReference

public class CSSReference extends WebReference<CSSReference> {
    public CSSReference(String name, Double version, String url) {
        super(name, version, url);
    }
}

JavascriptReference

public class JavascriptReference extends WebReference<JavascriptReference> {
    public JavascriptReference(String name, Double version, String url) {
        super(name, version, url);
    }
}

Annotations

Plugin/Component Metadata

@ComponentInformation(
    name = "My Component",
    description = "Component description",
    url = "https://example.com"
)
public class MyComponent { }

@PluginInformation(
    pluginName = "My Plugin",
    pluginUniqueName = "my-plugin",
    pluginVersion = "1.0.0"
)
public class MyPlugin { }

@FeatureInformation(
    name = "My Feature",
    description = "Feature description"
)
public class MyFeature { }

Page Configuration

@PageConfiguration(url = "/dashboard")
public class DashboardPage extends Page<DashboardPage> { }

HTML Child Constraints

Type-safe parent/child relationships:

| Interface | Purpose | |---|---| | GlobalChildren | Elements accepting any flow/phrasing content | | GlobalFeatures | Global HTML features | | HTMLFeatures | HTML-specific features | | FormChildren | Elements valid inside <form> | | NoClosingTag | Void elements (<br>, <img>) | | NoIDTag | Elements without id attribute | | NoClassAttribute | Elements without class attribute |

Exception Types

InvalidRequestException      // Malformed AJAX request
MissingComponentException    // Component not found in tree
NullComponentException       // Null component where required
NoServletFoundException      // No handler for request path
UserSecurityException        // Security violation

Browser/Client Types

Browsers                     // Enum of known browsers
BrowserGroups                // Grouping by engine/vendor
CSSVersions                  // CSS specification levels
HTMLVersions                 // HTML specification levels
HttpMethodTypes              // HTTP methods (GET, POST, etc.)

Configuration

JWebMPClientConfiguration enables:

| Setting | Value | Purpose | |---|---|---| | classpathScanning | true | Scan for SPI implementations | | annotationScanning | true | Enable annotation discovery | | fieldInfo | true | Collect field metadata | | methodInfo | true | Collect method metadata | | allowPaths | true | Path-based scanning |

JPMS Module

module com.jwebmp.client {
    requires transitive com.guicedee.client;
    requires transitive com.guicedee.jsonrepresentation;
    requires net.sf.uadetector.core;

    exports com.jwebmp.core.base.ajax;
    exports com.jwebmp.core.base.interfaces;
    exports com.jwebmp.core.services;
    exports com.jwebmp.interception.services;

    provides IGuiceModule with JWebMPClientBinder;
    provides IGuiceConfigurator with JWebMPClientConfiguration;

    uses AjaxCallIntercepter;
    uses DataCallIntercepter;
    uses SiteCallIntercepter;
}

Key Classes

  • AjaxCall — Incoming AJAX request payload (CallScope-scoped)
  • AjaxResponse — Outgoing AJAX response with updates/reactions
  • JavaScriptPart — Base for all JSON-serializable options
  • JWebMPClientBinder — Guice module binding interceptors and AJAX pipeline
  • HeadersDTO — Typed HTTP header wrapper

Common Patterns

Custom Interceptor Chain

// Security interceptor (runs first)
public class SecurityInterceptor implements SiteCallIntercepter<SecurityInterceptor> {
    @Override
    public void intercept(AjaxCall<?> call, AjaxResponse<?> response) {
        if (!authenticated(call)) {
            throw new UserSecurityException("Unauthorized");
        }
    }

    @Override
    public Integer sortOrder() {
        return 1;  // Run early
    }
}

// Logging interceptor (runs later)
public class LoggingInterceptor implements AjaxCallIntercepter<LoggingInterceptor> {
    @Override
    public void intercept(AjaxCall<?> call, AjaxResponse<?> response) {
        log.info("Processing event: {}", call.getEventType());
    }

    @Override
    public Integer sortOrder() {
        return 100;
    }
}

Dynamic Script Provider

public class InlineScript implements ScriptProvider {
    @Override
    public IComponentHierarchyBase<?, ?> produceScript() {
        Script<?> script = new Script<>();
        script.setText("console.log('Dynamic script loaded');");
        return script;
    }
}

Installation

<dependency>
  <groupId>com.jwebmp</groupId>
  <artifactId>jwebmp-client</artifactId>
</dependency>

References

  • Module: com.jwebmp.client
  • Java: 25+
  • Dependencies: GuicedEE Client, Jackson, UADetector
  • License: Apache 2.0