[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6ACP1SDC7TG2SC8S2S4PDDG":3,"skill-same:01M6ACP1SDC7TG2SC8S2S4PDDG":53},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":20,"linkOnly":21,"markdownContent":22,"markdownBody":22,"frontMatter":23,"refName":24,"commitSha":25,"skillDirectory":26,"forksCount":27,"repositoryHtmlUrl":28,"repositoryHomepageUrl":29,"ownerHtmlUrl":30,"ownerBlogUrl":31,"ownerDescription":6,"updatedAt":32,"install":33,"resources":46},"01M6ACP1SDC7TG2SC8S2S4PDDG","manage-java-events",null,"apache\u002Fofbiz-plugins","apache","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F47359?v=4","01M6AC83E6K4FDKK7HZ8WNMQKE","The Apache Software Foundation","01M6ACMJJ1Q4WYATY4GXZ2FAVD",178,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fapache\u002Fofbiz-plugins\u002Fblob\u002Ftrunk\u002Fai-agent-skills\u002Fmanage-java-events\u002FSKILL.md",[],[18,19],"java","manage java events","2026-11-10 10:12:30",false,"\u003C!--\nLicensed to the Apache Software Foundation (ASF) under one\nor more contributor license agreements.  See the NOTICE file\ndistributed with this work for additional information\nregarding copyright ownership.  The ASF licenses this file\nto you under the Apache License, Version 2.0 (the\n\"License\"); you may not use this file except in compliance\nwith the License.  You may obtain a copy of the License at\n\nhttp:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0\n\nUnless required by applicable law or agreed to in writing,\nsoftware distributed under the License is distributed on an\n\"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\nKIND, either express or implied.  See the License for the\nspecific language governing permissions and limitations\nunder the License.\n-->\n\n---\nname: manage-java-events\ndescription: |-\n  - Implementing OFBiz Events (web boundaries) in Java.\n  - Handling HTTP requests, responses, and MVC flow.\n  - Requires manage-java-patterns for shared standards.\n---\n\n# Skill: manage-java-events\n## Goal\nImplement robust web request handling and control flow using Java Events in OFBiz. This is particularly suited for complex web requests requiring integration with Java libraries or complex pre-processing before calling services.\n\n## Triggers\n**ALWAYS** read this skill when:\n- Creating or modifying Java methods intended to be called as events by `controller.xml`.\n- Interacting directly with `HttpServletRequest` or `HttpServletResponse` in Java.\n\n> [!TIP]\n> **See [manage-java-patterns](..\u002Fmanage-java-patterns\u002FSKILL.md)** for shared Java standards (logging, localization, and exception handling).\n\n---\n\n## Rules & Procedures\n\n### 1. Class Structure & Method Signature\n- **Static Methods**: A Java Event must be a `public static` method.\n- **Signature**: `public static String myEvent(HttpServletRequest request, HttpServletResponse response)`.\n- **Return Type**: Always returns a `String` mapping to `\u003Cresponse>` in `controller.xml` (e.g., `\"success\"`, `\"error\"`, `\"none\"`).\n\n### 2. Context & Input Handling\n- **Delegator\u002FDispatcher**: Retrieve from request attributes.\n  ```java\n  Delegator delegator = (Delegator) request.getAttribute(\"delegator\");\n  LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute(\"dispatcher\");\n  ```\n- **Security Context**: Fetch `userLogin`, `locale`, and `timeZone` from request attributes\u002Fsession.\n  ```java\n  GenericValue userLogin = (GenericValue) request.getAttribute(\"userLogin\");\n  if (userLogin == null) {\n      userLogin = (GenericValue) request.getSession().getAttribute(\"userLogin\");\n  }\n  Locale locale = UtilHttp.getLocale(request);\n  TimeZone timeZone = UtilHttp.getTimeZone(request);\n  ```\n  - **Rule**: Derive identity from the `userLogin` object, never from parameters. \n- **Parameters**: Use `request.getParameter(\"id\")` for single values or `UtilHttp.getParameterMap(request)` for bulk.\n- **Rule**: Consult **[manage-java-patterns](..\u002Fmanage-java-patterns\u002FSKILL.md)** for mandatory trimming and validation before passing data to services.\n\n### 3. Response & Message Management\n- **Success Pattern**: Set `_EVENT_MESSAGE_` and return `\"success\"`.\n- **Error Pattern**: Set `_ERROR_MESSAGE_` or `_ERROR_MESSAGE_LIST_` and return `\"error\"`.\n- **AJAX\u002FJSON Boundary**: \n    - Set `response.setContentType(\"application\u002Fjson\")` and HTTP status.\n    - Write JSON directly to `response.getWriter()`.\n    - **MUST** return `\"none\"` to stop further view rendering.\n- **Redirection**: For `request-redirect`, store messages in the session (`request.getSession().setAttribute(\"_ERROR_MESSAGE_\", ...)`) to ensure they survive the redirect.\n\n### 4. Uploads & Streaming\n- **Multipart Data**: Standard `request.getParameter()` returns `null` for file uploads.\n    - **Rule**: Use OFBiz upload utilities or request wrappers.\n    - **Rule**: Never read full files into memory; process as a stream.\n- **Direct Streaming**: For file downloads, write directly to the response output stream and return `\"none\"`.\n\n---\n\n## Guardrails\n- **Standard Response Strings**: Stick to `\"success\"`, `\"error\"`, or `\"none\"`.\n- **Return \"none\"**: Required whenever the response is already committed (JSON, redirects, streaming).\n- **Localization**: ALL user-facing messages MUST be localized. Refer to **[manage-java-patterns](..\u002Fmanage-java-patterns\u002FSKILL.md)**.\n- **Validation & Exceptions**: Refer to **[manage-java-patterns](..\u002Fmanage-java-patterns\u002FSKILL.md)** for standard `UtilValidate` usage and exception strategies.\n- **CSRF & Identity**: Ensure CSRF protection for state-changing requests. Derive identity from `userLogin` to prevent spoofing.\n\n## Bad Practices & Anti-patterns\n\n### Anti-pattern: The \"Fat Event\" (Deep Business Logic)\n**Pitfall**: Writing business logic (loops, calculations, direct DB commits) inside a Java Event. This makes the logic non-reusable and bypasses the service engine's transaction and ECA management.\n- **❌ Bad (Logic in Event)**:\n  ```java\n  for (String id : ids) {\n      GenericValue gv = delegator.findOne(\"MyEntity\", UtilMisc.toMap(\"id\", id), false);\n      gv.set(\"statusId\", \"DONE\");\n      gv.store();\n  }\n  return \"success\";\n  ```\n- **✅ Good (Delegate to Service)**:\n  ```java\n  \u002F\u002F Event only prepares input and calls service\n  Map\u003CString, Object> result = dispatcher.runSync(\"updateMyStatus\", UtilMisc.toMap(\"ids\", ids, \"userLogin\", userLogin));\n  return ServiceUtil.isError(result) ? \"error\" : \"success\";\n  ```\n\n### Anti-pattern: Identity Spoofing (Trusting Parameters)\n**Pitfall**: Using an ID passed in the request parameter (e.g., `partyId`, `userId`) to determine the identity or permissions of the caller. Parameters can be easily spoofed.\n- **❌ Bad**:\n  ```java\n  String partyId = request.getParameter(\"partyId\");\n  \u002F\u002F Security Risk: User can pass any partyId in the URL!\n  GenericValue profile = delegator.findOne(\"Party\", UtilMisc.toMap(\"partyId\", partyId), false);\n  ```\n- **✅ Good**: \n  ```java\n  \u002F\u002F Use the verified userLogin object from the session\u002Frequest\n  String partyId = userLogin.getString(\"partyId\"); \n  ```\n\n### Anti-pattern: Standard Response Bypass\n**Pitfall**: Returning non-standard strings or hardcoded message literals instead of using the controller flow and localized labels.\n- **❌ Bad**:\n  ```java\n  if (failure) return \"Something went wrong\"; \u002F\u002F Breaks controller mapping\n  ```\n- **✅ Good**: \n  ```java\n  if (failure) {\n      request.setAttribute(\"_ERROR_MESSAGE_\", UtilProperties.getMessage(...));\n      return \"error\"; \u002F\u002F Correctly enters the error flow defined in controller.xml\n  }\n  ```\n\n### Other Bad Practices\n- **Assuming Form EncTypes**: Using `request.getParameter()` with `multipart\u002Fform-data` (returns `null`).\n- **Memory-Heavy Uploads**: Reading a full `InputStream` into memory; always use streaming.\n- **Heavy Sessions**: Storing large, non-essential objects in `HttpSession`.\n- **Assuming Auto-Transactions**: Events do NOT handle transactions; use Services for atomicity.\n\n---\n\n## Common Code Patterns (Examples)\n\n### Standard Java Event (Delegating to Service)\nSee how this event extracts context, validates, and hands off to the dispatcher:\n```java\npublic static String processOrderEvent(HttpServletRequest request, HttpServletResponse response) {\n    LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute(\"dispatcher\");\n    Locale locale = UtilHttp.getLocale(request);\n    GenericValue userLogin = (GenericValue) request.getAttribute(\"userLogin\");\n    \n    String productId = request.getParameter(\"productId\");\n    if (UtilValidate.isEmpty(productId)) {\n        request.setAttribute(\"_ERROR_MESSAGE_\", UtilProperties.getMessage(\"UiLabels\", \"MissingProduct\", locale));\n        return \"error\";\n    }\n\n    try {\n        Map\u003CString, Object> serviceCtx = UtilMisc.toMap(\"productId\", productId, \"userLogin\", userLogin, \"locale\", locale);\n        Map\u003CString, Object> result = dispatcher.runSync(\"myOrderService\", serviceCtx);\n        if (ServiceUtil.isError(result)) {\n            request.setAttribute(\"_ERROR_MESSAGE_\", ServiceUtil.getErrorMessage(result));\n            return \"error\";\n        }\n    } catch (Exception e) {\n        Debug.logError(e, \"Event Failure\", MODULE);\n        request.setAttribute(\"_ERROR_MESSAGE_\", \"System Error\");\n        return \"error\";\n    }\n    \n    request.setAttribute(\"_EVENT_MESSAGE_\", \"Success\");\n    return \"success\";\n}\n```\n\n### Redirect with Session-Based Messages\nWhen using `request-redirect`, messages must be stored in the session to survive the redirect:\n```java\npublic static String redirectWithMsg(HttpServletRequest request, HttpServletResponse response) {\n    Locale locale = UtilHttp.getLocale(request);\n    String msg = UtilProperties.getMessage(\"MyLabels\", \"FormSubmitted\", locale);\n    \u002F\u002F Store in session for visibility after redirect\n    request.getSession().setAttribute(\"_EVENT_MESSAGE_\", msg);\n    return \"request-redirect\";\n}\n```\n\n### Direct JSON Response for AJAX\n```java\npublic static String returnJsonResponse(HttpServletRequest request, HttpServletResponse response) {\n    Map\u003CString, Object> data = UtilMisc.toMap(\"status\", \"ok\", \"timestamp\", UtilDateTime.nowTimestamp());\n    try {\n        response.setContentType(\"application\u002Fjson\");\n        response.setCharacterEncoding(\"UTF-8\");\n        response.setHeader(\"Cache-Control\", \"no-store\");\n        response.setStatus(HttpServletResponse.SC_OK);\n        \n        \u002F\u002F Use framework JSON utilities to serialize\u002Fwrite\n        \u002F\u002F E.g., JSON.from(data).write(response.getWriter());\n        String json = \u002F\u002F ... framework serialization ...\n        response.getWriter().write(json);\n        response.getWriter().flush();\n    } catch (Exception e) {\n        Debug.logError(e, \"Error writing JSON response\", MODULE);\n        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n    }\n    return \"none\"; \u002F\u002F Stop further view rendering\n}\n```\n",{},"trunk","6c6c27710443de03e9247605d33a2e2f5bc3d1e2","ai-agent-skills\u002Fmanage-java-events",222,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fofbiz-plugins","https:\u002F\u002Fofbiz.apache.org\u002F","https:\u002F\u002Fgithub.com\u002Fapache","https:\u002F\u002Fwww.apache.org\u002F","2026-11-12 00:54:27",{"source":7,"skillName":5,"commands":34,"prompt":44,"zipAvailable":45},[35,38,41],{"tool":36,"command":37},"npx","npx skills add apache\u002Fofbiz-plugins --skill manage-java-events",{"tool":39,"command":40},"bunx","bunx skills add apache\u002Fofbiz-plugins --skill manage-java-events",{"tool":42,"command":43},"pnpm","pnpm dlx skills add apache\u002Fofbiz-plugins --skill manage-java-events","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fapache\u002Fofbiz-plugins\u002Fblob\u002Ftrunk\u002Fai-agent-skills\u002Fmanage-java-events\u002FSKILL.md",true,[47],{"path":48,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":51,"sourceHtmlUrl":52},"suggest-compact.sh","SCRIPT","脚本",1634,"https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fdevelopment\u002Fcc-skill-strategic-compact\u002Fsuggest-compact.sh",[54,61,68,75,82,89],{"skillNo":55,"name":56,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":57,"categories":58,"keywords":59,"publishedAt":20},"01M6ACQSN90YKJZ92MQ52D9R8H","prepush-readiness","https:\u002F\u002Fgithub.com\u002Fapache\u002Fofbiz-plugins\u002Fblob\u002Ftrunk\u002Fai-agent-skills\u002Fprepush-readiness\u002FSKILL.md",[],[60],"prepush readiness",{"skillNo":62,"name":63,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":66,"publishedAt":20},"01M6ACQPR1WRA751V4DXVC3AHF","manage-webapps","https:\u002F\u002Fgithub.com\u002Fapache\u002Fofbiz-plugins\u002Fblob\u002Ftrunk\u002Fai-agent-skills\u002Fmanage-webapps\u002FSKILL.md",[],[67],"manage webapps",{"skillNo":69,"name":70,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":73,"publishedAt":20},"01M6ACQMCX8Z7NY2CE08NCV6ND","manage-themes","https:\u002F\u002Fgithub.com\u002Fapache\u002Fofbiz-plugins\u002Fblob\u002Ftrunk\u002Fai-agent-skills\u002Fmanage-themes\u002FSKILL.md",[],[74],"manage themes",{"skillNo":76,"name":77,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":80,"publishedAt":20},"01M6ACQHYN20AFWF6TKK4RGQ8A","manage-tests","https:\u002F\u002Fgithub.com\u002Fapache\u002Fofbiz-plugins\u002Fblob\u002Ftrunk\u002Fai-agent-skills\u002Fmanage-tests\u002FSKILL.md",[],[81],"manage tests",{"skillNo":83,"name":84,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":87,"publishedAt":20},"01M6ACQFRMW7R45YD3HRAXPGWF","manage-templates","https:\u002F\u002Fgithub.com\u002Fapache\u002Fofbiz-plugins\u002Fblob\u002Ftrunk\u002Fai-agent-skills\u002Fmanage-templates\u002FSKILL.md",[],[88],"manage templates",{"skillNo":90,"name":91,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":94,"publishedAt":20},"01M6ACQCSSDZQMD8VX2K3WRR93","manage-strategies","https:\u002F\u002Fgithub.com\u002Fapache\u002Fofbiz-plugins\u002Fblob\u002Ftrunk\u002Fai-agent-skills\u002Fmanage-strategies\u002FSKILL.md",[],[95],"manage strategies"]