d8ffaac1 by Ean Schuessler

Fix duplicate field declarations in EnhancedMcpServlet

1 parent 8489faad
...@@ -46,17 +46,23 @@ class EnhancedMcpServlet extends HttpServlet { ...@@ -46,17 +46,23 @@ class EnhancedMcpServlet extends HttpServlet {
46 46
47 private JsonSlurper jsonSlurper = new JsonSlurper() 47 private JsonSlurper jsonSlurper = new JsonSlurper()
48 48
49 // Session state constants 49 // Session state constants
50 private static final int STATE_UNINITIALIZED = 0 50 private static final int STATE_UNINITIALIZED = 0
51 private static final int STATE_INITIALIZING = 1 51 private static final int STATE_INITIALIZING = 1
52 private static final int STATE_INITIALIZED = 2 52 private static final int STATE_INITIALIZED = 2
53 53
54 // Simple registry for active connections only (transient HTTP connections) 54 // Simple registry for active connections only (transient HTTP connections)
55 private final Map<String, PrintWriter> activeConnections = new ConcurrentHashMap<>() 55 private final Map<String, PrintWriter> activeConnections = new ConcurrentHashMap<>()
56 56
57 // Session management using Moqui's Visit system directly 57 // Session management using Moqui's Visit system directly
58 // No need for separate session manager - Visit entity handles persistence 58 // No need for separate session manager - Visit entity handles persistence
59 private final Map<String, Integer> sessionStates = new ConcurrentHashMap<>() 59 private final Map<String, Integer> sessionStates = new ConcurrentHashMap<>()
60
61 // Message storage for notifications/subscribe and notifications/unsubscribe
62 private final Map<String, List<Map>> sessionMessages = new ConcurrentHashMap<>()
63
64 // In-memory session tracking to avoid database access for read operations
65 private final Map<String, String> sessionUsers = new ConcurrentHashMap<>()
60 66
61 // Progress tracking for notifications/progress 67 // Progress tracking for notifications/progress
62 private final Map<String, Map> sessionProgress = new ConcurrentHashMap<>() 68 private final Map<String, Map> sessionProgress = new ConcurrentHashMap<>()
...@@ -64,15 +70,6 @@ class EnhancedMcpServlet extends HttpServlet { ...@@ -64,15 +70,6 @@ class EnhancedMcpServlet extends HttpServlet {
64 // Visit cache to reduce database access and prevent lock contention 70 // Visit cache to reduce database access and prevent lock contention
65 private final Map<String, EntityValue> visitCache = new ConcurrentHashMap<>() 71 private final Map<String, EntityValue> visitCache = new ConcurrentHashMap<>()
66 72
67 // In-memory session tracking to avoid database access for read operations
68 private final Map<String, String> sessionUsers = new ConcurrentHashMap<>()
69
70 // Message storage for notifications/message
71 private final Map<String, List<Map>> sessionMessages = new ConcurrentHashMap<>()
72
73 // Subscription tracking for notifications/subscribe and notifications/unsubscribe
74 private final Map<String, Set<String>> sessionSubscriptions = new ConcurrentHashMap<>()
75
76 // Notification queue for server-initiated notifications (for non-SSE clients) 73 // Notification queue for server-initiated notifications (for non-SSE clients)
77 private static final Map<String, List<Map>> notificationQueues = new ConcurrentHashMap<>() 74 private static final Map<String, List<Map>> notificationQueues = new ConcurrentHashMap<>()
78 75
...@@ -511,18 +508,32 @@ class EnhancedMcpServlet extends HttpServlet { ...@@ -511,18 +508,32 @@ class EnhancedMcpServlet extends HttpServlet {
511 private void handleJsonRpc(HttpServletRequest request, HttpServletResponse response, ExecutionContextImpl ec, String webappName, String requestBody, def visit) 508 private void handleJsonRpc(HttpServletRequest request, HttpServletResponse response, ExecutionContextImpl ec, String webappName, String requestBody, def visit)
512 throws IOException { 509 throws IOException {
513 510
514 // Initialize web facade for proper session management (like SSE connections) 511 // Initialize web facade for proper session management
515 // This prevents the null user loop by ensuring HTTP session is properly linked
516 try { 512 try {
517 // If we have a visit, make sure it's in the request/session before initWebFacade 513 // If we have a visit, use it directly (don't create new one)
514 visit = ec.user.getVisit()
518 if (visit) { 515 if (visit) {
519 request.setAttribute("moqui.visitId", visit.visitId)
520 request.getSession().setAttribute("moqui.visitId", visit.visitId) 516 request.getSession().setAttribute("moqui.visitId", visit.visitId)
517 logger.debug("JSON-RPC web facade initialized for user: ${ec.user?.username} with visit: ${visit.visitId}")
518 } else {
519 // No visit exists, need to create one
520 logger.info("Creating new Visit record for user: ${ec.user?.username}")
521 visit = ec.entity.makeValue("moqui.server.Visit")
522 visit.visitId = ec.userFacade.getVisitId(visit)
523 visit.userId = ec.user.userId
524 visit.sessionId = visit.sessionId
525 visit.userAccountId = ec.user.userAccount?.userAccountId
526 visit.sessionCreatedDate = ec.user.nowTimestamp
527 visit.visitStatus = null
528 visit.lastActiveDate = ec.user.nowTimestamp
529 visit.visitDeletedDate = null
530 ec.entity.create(visit)
531 logger.info("Visit ${visit.visitId} created for user: ${ec.user?.username}")
521 } 532 }
522 ec.initWebFacade(webappName, request, response) 533 ec.initWebFacade(webappName, request, response)
523 logger.debug("JSON-RPC web facade initialized for user: ${ec.user?.username} with visit: ${ec.user.visitId}") 534 logger.debug("JSON-RPC web facade initialized for user: ${ec.user?.username} with visit: ${visit.visitId}")
524 } catch (Exception e) { 535 } catch (Exception e) {
525 logger.warn("JSON-RPC web facade initialization failed: ${e.message}") 536 logger.warn("Web facade initialization warning: ${e.message}")
526 // Continue anyway - we may still have basic user context from auth 537 // Continue anyway - we may still have basic user context from auth
527 } 538 }
528 539
...@@ -532,13 +543,13 @@ class EnhancedMcpServlet extends HttpServlet { ...@@ -532,13 +543,13 @@ class EnhancedMcpServlet extends HttpServlet {
532 logger.info("Enhanced MCP JSON-RPC Request: ${method} ${request.requestURI} - Accept: ${acceptHeader}") 543 logger.info("Enhanced MCP JSON-RPC Request: ${method} ${request.requestURI} - Accept: ${acceptHeader}")
533 544
534 // Validate Accept header per MCP 2025-11-25 spec requirement #2 545 // Validate Accept header per MCP 2025-11-25 spec requirement #2
535 // Client MUST include Accept header with either application/json or text/event-stream 546 // Client MUST include Accept header with at least one of: application/json or text/event-stream
536 if (!acceptHeader || !(acceptHeader.contains("application/json") || acceptHeader.contains("text/event-stream"))) { 547 if (!acceptHeader || !acceptHeader.contains("application/json") && !acceptHeader.contains("text/event-stream")) {
537 response.setStatus(HttpServletResponse.SC_BAD_REQUEST) 548 response.setStatus(HttpServletResponse.SC_BAD_REQUEST)
538 response.setContentType("application/json") 549 response.setContentType("application/json")
539 response.writer.write(JsonOutput.toJson([ 550 response.writer.write(JsonOutput.toJson([
540 jsonrpc: "2.0", 551 jsonrpc: "2.0",
541 error: [code: -32600, message: "Accept header must include application/json and text/event-stream"], 552 error: [code: -32600, message: "Accept header must include application/json or text/event-stream"],
542 id: null 553 id: null
543 ])) 554 ]))
544 return 555 return
......