This is an automated email from the ASF dual-hosted git repository.
dklco pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-app-cms.git
The following commit(s) were added to refs/heads/master by this push:
new 60dec6c SLING-11102 - Adding a console for getting the query plan for
queries and seeing the top / slowest queries
60dec6c is described below
commit 60dec6c451152b12b0bb0579ea6b5731163ad795
Author: Dan Klco <[email protected]>
AuthorDate: Sun Jan 30 17:11:49 2022 -0500
SLING-11102 - Adding a console for getting the query plan for queries and
seeing the top / slowest queries
---
core/pom.xml | 9 ++
.../sling/cms/core/models/QueryDebugger.java | 147 +++++++++++++++++++++
.../sling/cms/core/models/QueryDebuggerTest.java | 64 +++++++++
i18n-helper/i18n/de.json | 12 +-
i18n-helper/i18n/zh.json | 11 +-
i18n-helper/src/messages.json | 10 +-
pom.xml | 7 +-
ui/bnd.bnd | 2 +-
ui/src/main/frontend/scss/cms.scss | 4 +
.../components/cms/querydebug/querydebug.jsp | 103 +++++++++++++++
.../libs/sling-cms/content/admin/querydebug.json | 66 +++++++++
.../jcr_root/libs/sling-cms/content/start.json | 6 +
.../resources/jcr_root/libs/sling-cms/i18n.json | 2 +-
13 files changed, 435 insertions(+), 8 deletions(-)
diff --git a/core/pom.xml b/core/pom.xml
index 06f7f3f..e4108a2 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -187,6 +187,10 @@
<artifactId>org.apache.sling.testing.sling-mock.junit4</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.sling-mock-oak</artifactId>
+ </dependency>
+ <dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
@@ -206,5 +210,10 @@
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</dependency>
+ <dependency>
+ <groupId>javax.management.j2ee</groupId>
+ <artifactId>javax.management.j2ee-api</artifactId>
+ </dependency>
+
</dependencies>
</project>
\ No newline at end of file
diff --git
a/core/src/main/java/org/apache/sling/cms/core/models/QueryDebugger.java
b/core/src/main/java/org/apache/sling/cms/core/models/QueryDebugger.java
new file mode 100644
index 0000000..7ce7012
--- /dev/null
+++ b/core/src/main/java/org/apache/sling/cms/core/models/QueryDebugger.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.cms.core.models;
+
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.Row;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.Self;
+import org.osgi.annotation.versioning.ProviderType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ProviderType
+@Model(adaptables = SlingHttpServletRequest.class)
+public class QueryDebugger {
+
+ private static final String MBEAN_NAME =
"org.apache.jackrabbit.oak:name=Oak Query Statistics,type=QueryStat";
+
+ private static final Logger log =
LoggerFactory.getLogger(QueryDebugger.class);
+ private final String plan;
+ private final String exception;
+ private final String statement;
+ private final List<Map<String, Object>> slowQueries = new ArrayList<>();
+ private final List<Map<String, Object>> popularQueries = new ArrayList<>();
+
+ @Inject
+ public QueryDebugger(@Self SlingHttpServletRequest request) {
+
+ Optional<String> statementParam =
Optional.ofNullable(request.getParameter("statement"));
+ String language =
Optional.ofNullable(request.getParameter("language")).orElse(Query.JCR_SQL2);
+
+ String lplan = null;
+ String lexception = null;
+ String lstatement = null;
+ try {
+ if (statementParam.isPresent()) {
+
+ QueryManager queryManager =
request.getResourceResolver().adaptTo(Session.class).getWorkspace()
+ .getQueryManager();
+ Query query = queryManager.createQuery("explain " +
statementParam.get(), language);
+ Row row = query.execute().getRows().nextRow();
+ lplan = row.getValue("plan").getString();
+ lstatement = statementParam.get();
+ }
+ } catch (RepositoryException re) {
+ lexception = re.toString();
+ log.warn("Failed to debug query: {}", statementParam, re);
+ } finally {
+ this.plan = lplan;
+ this.exception = lexception;
+ this.statement = lstatement;
+ }
+
+ try {
+ collectMbeanData("PopularQueries", popularQueries);
+ collectMbeanData("SlowQueries", slowQueries);
+ } catch (MBeanException | MalformedObjectNameException |
InstanceNotFoundException | AttributeNotFoundException
+ | NullPointerException | ReflectionException e) {
+ log.warn("Failed to load mBean data", e);
+ }
+ }
+
+ private void collectMbeanData(String attributeName, List<Map<String,
Object>> target)
+ throws MalformedObjectNameException, NullPointerException,
InstanceNotFoundException,
+ AttributeNotFoundException, ReflectionException, MBeanException {
+ MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+ ObjectName mbeanName = ObjectName.getInstance(MBEAN_NAME);
+ TabularData data = (TabularData) mBeanServer.getAttribute(mbeanName,
attributeName);
+ data.values().stream().map(CompositeData.class::cast)
+ .forEach(compositeData ->
target.add(compositeData.getCompositeType().keySet().stream()
+ .collect(Collectors.toMap(k -> k,
compositeData::get))));
+ }
+
+ /**
+ * @return the plan
+ */
+ public String getPlan() {
+ return plan;
+ }
+
+ /**
+ * @return the exception
+ */
+ public String getException() {
+ return exception;
+ }
+
+ /**
+ * @return the statement
+ */
+ public String getStatement() {
+ return statement;
+ }
+
+ /**
+ * @return the slowQueries
+ */
+ public List<Map<String, Object>> getSlowQueries() {
+ return slowQueries;
+ }
+
+ /**
+ * @return the popularQueries
+ */
+ public List<Map<String, Object>> getPopularQueries() {
+ return popularQueries;
+ }
+
+}
diff --git
a/core/src/test/java/org/apache/sling/cms/core/models/QueryDebuggerTest.java
b/core/src/test/java/org/apache/sling/cms/core/models/QueryDebuggerTest.java
new file mode 100644
index 0000000..85a1159
--- /dev/null
+++ b/core/src/test/java/org/apache/sling/cms/core/models/QueryDebuggerTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.cms.core.models;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import javax.jcr.query.Query;
+
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class QueryDebuggerTest {
+
+ @Rule
+ public SlingContext context = new
SlingContext(ResourceResolverType.JCR_OAK);
+
+ @Test
+ public void testNoParams() {
+ QueryDebugger debugger = new QueryDebugger(context.request());
+ assertNull(debugger.getException());
+ assertNull(debugger.getPlan());
+ assertNull(debugger.getStatement());
+ }
+
+ @Test
+ public void testExplain() {
+ context.request().addRequestParameter("statement", "SELECT * FROM
[nt:base]");
+ context.request().addRequestParameter("language", Query.JCR_SQL2);
+
+ QueryDebugger debugger = new QueryDebugger(context.request());
+ assertNotNull(debugger.getPlan());
+ assertNull(debugger.getException());
+ assertNotNull(debugger.getStatement());
+ }
+
+ @Test
+ public void testFailure() {
+ context.request().addRequestParameter("statement", "SELECT * FROM
[nt:base]");
+ context.request().addRequestParameter("language", Query.XPATH);
+
+ QueryDebugger debugger = new QueryDebugger(context.request());
+ assertNull(debugger.getPlan());
+ assertNotNull(debugger.getException());
+ assertNull(debugger.getStatement());
+ }
+
+}
diff --git a/i18n-helper/i18n/de.json b/i18n-helper/i18n/de.json
index 7b14d02..602cec3 100644
--- a/i18n-helper/i18n/de.json
+++ b/i18n-helper/i18n/de.json
@@ -254,7 +254,7 @@
"JSON": "JSON",
"Job": "Job",
"Jobs": "Jobs",
- "Job Name": "Name des Jobs",
+ "Job Name": "Name des Jobs",
"Jpeg": "Jpeg",
"Key": "Schlüssel",
"Keywords": "Schlüsselwort",
@@ -590,5 +590,13 @@
"i18n Dictionaries": "I18N-Verzeichnisse",
"i18n Dictionarties": "I18N-Verzeichnisse",
"i18n Dictionary": "I18N-Verzeichnis",
- "i18n Dictionary Entries": "I18N-Verzeichniseinträge"
+ "i18n Dictionary Entries": "I18N-Verzeichniseinträge",
+ "Debug Query": "",
+ "Duration": "",
+ "Plan": "",
+ "Popular Queries": "",
+ "Query Debugger": "",
+ "Query Language": "",
+ "Query Statement": "",
+ "Slow Queries": ""
}
\ No newline at end of file
diff --git a/i18n-helper/i18n/zh.json b/i18n-helper/i18n/zh.json
index f3554e4..8c594fd 100644
--- a/i18n-helper/i18n/zh.json
+++ b/i18n-helper/i18n/zh.json
@@ -593,12 +593,19 @@
"Existing Path": "存在路径",
"Replacing properties under path": "替换此路径内所有资源的属性",
"Select Position": "选择位置",
- "Job Name": "",
"None": "",
"Create": "",
"Delete Rendition": "",
"Download Rendition": "",
"Rendition": "",
"Rendition Name": "",
- "Renditions": ""
+ "Renditions": "",
+ "Debug Query": "",
+ "Duration": "",
+ "Plan": "",
+ "Popular Queries": "",
+ "Query Debugger": "",
+ "Query Language": "",
+ "Query Statement": "",
+ "Slow Queries": ""
}
\ No newline at end of file
diff --git a/i18n-helper/src/messages.json b/i18n-helper/src/messages.json
index 7f08fcd..ac46de5 100644
--- a/i18n-helper/src/messages.json
+++ b/i18n-helper/src/messages.json
@@ -116,6 +116,7 @@
"Date",
"Date/Time",
"Datetime Local",
+ "Debug Query",
"Default",
"Default File Editor",
"Default Layout",
@@ -153,6 +154,7 @@
"Download Rendition",
"Download file",
"Drop invalid queue items",
+ "Duration",
"Edit",
"Edit Configuration",
"Edit Configuration Properties",
@@ -254,7 +256,7 @@
"JSON",
"Job",
"Jobs",
- "Job Name",
+ "Job Name",
"Jpeg",
"Key",
"Keywords",
@@ -359,8 +361,10 @@
"Paths",
"Pattern",
"Placeholder",
+ "Plan",
"Policy",
"Policy Path",
+ "Popular Queries",
"Position",
"Predecessors",
"Preview",
@@ -388,6 +392,9 @@
"Published Date",
"Pull Items",
"Query",
+ "Query Debugger",
+ "Query Language",
+ "Query Statement",
"Queue",
"Queue Processing Enabled",
"Queue Provider",
@@ -489,6 +496,7 @@
"Sling CMS - Transformation Size Handler",
"Sling CMS - Transformations Configuration",
"Sling CMS Thumbnails",
+ "Slow Queries",
"Start",
"Start Job",
"Started",
diff --git a/pom.xml b/pom.xml
index 13ec543..df1cc49 100644
--- a/pom.xml
+++ b/pom.xml
@@ -331,6 +331,12 @@
<version>${osgi-annotation-version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>javax.management.j2ee</groupId>
+ <artifactId>javax.management.j2ee-api</artifactId>
+ <version>1.1.2</version>
+ <scope>provided</scope>
+ </dependency>
<!-- Document Processing Dependencies -->
<dependency>
<artifactId>jsoup</artifactId>
@@ -438,4 +444,3 @@
</pluginManagement>
</build>
</project>
-
diff --git a/ui/bnd.bnd b/ui/bnd.bnd
index d562761..ceeb2c7 100644
--- a/ui/bnd.bnd
+++ b/ui/bnd.bnd
@@ -1,3 +1,3 @@
Sling-Nodetypes: SLING-INF/nodetypes/nodetypes.cnd
-Sling-Initial-Content:
jcr_root;overwriteProperties=true;overwrite=false;ignoreImportProviders:=xml,jcr_root/conf/global;overwrite=true;ignoreImportProviders:=xml;path:=/conf/global,jcr_root/etc/clientlibs;overwrite=true;ignoreImportProviders:=xml;path:=/etc/clientlibs,jcr_root/etc/taxonomy;overwrite:=false;uninstall:=true;path:=/etc/taxonomy,jcr_root/oak%3Aindex;overwrite:=false;uninstall:=true;path:=/oak:index,jcr_root/libs/sling-cms;overwrite:=true;uninstall:=true;path:=/libs/sling-cm
[...]
+Sling-Initial-Content:
jcr_root;overwriteProperties:=true;overwrite:=false;ignoreImportProviders:=xml,jcr_root/conf/global;overwrite:=true;ignoreImportProviders:=xml;path:=/conf/global,jcr_root/etc/taxonomy;overwrite:=false;uninstall:=true;path:=/etc/taxonomy,jcr_root/oak%3Aindex;overwrite:=false;uninstall:=true;path:=/oak:index,jcr_root/libs/sling-cms;overwrite:=true;uninstall:=true;path:=/libs/sling-cms,jcr_root/libs/sling/thumbnails;overwrite:=true;uninstall:=true;path:=/libs/sling/th
[...]
-includeresource: target/frontend/dist
\ No newline at end of file
diff --git a/ui/src/main/frontend/scss/cms.scss
b/ui/src/main/frontend/scss/cms.scss
index c9d1463..a77dde7 100644
--- a/ui/src/main/frontend/scss/cms.scss
+++ b/ui/src/main/frontend/scss/cms.scss
@@ -261,6 +261,10 @@ nav .level-right {
padding: 1em;
}
+#query-debug {
+ height: calc(100vh - 500px);
+}
+
.rte-form {
margin: 0.5em 0;
}
diff --git
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/querydebug/querydebug.jsp
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/querydebug/querydebug.jsp
new file mode 100644
index 0000000..baa79c8
--- /dev/null
+++
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/querydebug/querydebug.jsp
@@ -0,0 +1,103 @@
+<%-- /*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */ --%>
+<%@include file="/libs/sling-cms/global.jsp"%>
+<div id="query-debug" class="scroll-container reload-container">
+ <sling:adaptTo adaptable="${slingRequest}"
adaptTo="org.apache.sling.cms.core.models.QueryDebugger" var="queryDebugger" />
+ <br/><hr/><br/>
+ <dl>
+ <c:if test="${not empty queryDebugger.statement}">
+ <dt><fmt:message key="Query Statement" /><dt>
+ <dd>${sling:encode(queryDebugger.statement,'HTML')}</dd>
+ </c:if>
+ <c:if test="${not empty queryDebugger.plan}">
+ <dt><fmt:message key="Plan" /><dt>
+ <dd>${sling:encode(queryDebugger.plan,'HTML')}</dd>
+ </c:if>
+ <c:if test="${not empty queryDebugger.exception}">
+ <dt><fmt:message key="Exception" /><dt>
+ <dd>${sling:encode(queryDebugger.exception,'HTML')}</dd>
+ </c:if>
+ <br/><hr/><br/>
+ <h2><fmt:message key="Popular Queries" /></h2>
+ <table class="table">
+ <tr>
+ <th>
+ <fmt:message key="Query Statement" />
+ </th>
+ <th>
+ <fmt:message key="Query Language" />
+ </th>
+ <th>
+ <fmt:message key="Count" />
+ </th>
+ <th>
+ <fmt:message key="Duration" />
+ </th>
+ </tr>
+ <c:forEach var="query" items="${queryDebugger.popularQueries}">
+ <tr>
+ <td>
+ ${query.statement}
+ </td>
+ <td>
+ ${query.language}
+ </td>
+ <td>
+ ${query.occurrenceCount}
+ </td>
+ <td>
+ ${query.duration}
+ </td>
+ </tr>
+ </c:forEach>
+ </table>
+ <h2><fmt:message key="Slow Queries" /></h2>
+ <table class="table">
+ <tr>
+ <th>
+ <fmt:message key="Query Statement" />
+ </th>
+ <th>
+ <fmt:message key="Query Language" />
+ </th>
+ <th>
+ <fmt:message key="Count" />
+ </th>
+ <th>
+ <fmt:message key="Duration" />
+ </th>
+ </tr>
+ <c:forEach var="query" items="${queryDebugger.slowQueries}">
+ <tr>
+ <td>
+ ${query.statement}
+ </td>
+ <td>
+ ${query.language}
+ </td>
+ <td>
+ ${query.occurrenceCount}
+ </td>
+ <td>
+ ${query.duration}
+ </td>
+ </tr>
+ </c:forEach>
+ </table>
+</div>
\ No newline at end of file
diff --git
a/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/querydebug.json
b/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/querydebug.json
new file mode 100644
index 0000000..7e6ebbc
--- /dev/null
+++
b/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/querydebug.json
@@ -0,0 +1,66 @@
+{
+ "jcr:primaryType": "sling:Page",
+ "jcr:content": {
+ "sling:resourceType": "sling-cms/components/pages/base",
+ "jcr:title": "Query Debugger",
+ "jcr:primaryType": "nt:unstructured",
+ "container": {
+ "jcr:primaryType": "nt:unstructured",
+ "sling:resourceType": "sling-cms/components/general/container",
+ "title": {
+ "jcr:primaryType": "nt:unstructured",
+ "sling:resourceType": "sling-cms/components/general/textelement",
+ "i18n": true,
+ "level": "h3",
+ "text": "Query Debugger"
+ },
+ "getform": {
+ "jcr:primaryType": "nt:unstructured",
+ "sling:resourceType": "sling-cms/components/cms/getform",
+ "button": "Debug Query",
+ "load": "#query-debug",
+ "target": "#query-debug",
+ "action":
"/libs/sling-cms/content/admin/querydebug/jcr:content/container/querydebug.html",
+ "skipcancel": true,
+ "fields": {
+ "jcr:primaryType": "nt:unstructured",
+ "sling:resourceType": "sling-cms/components/general/container",
+ "statement": {
+ "jcr:primaryType": "nt:unstructured",
+ "sling:resourceType":
"sling-cms/components/editor/fields/textarea",
+ "label": "Query Statement",
+ "name": "statement"
+ },
+ "language": {
+ "jcr:primaryType": "nt:unstructured",
+ "sling:resourceType": "sling-cms/components/editor/fields/select",
+ "label": "Query Language",
+ "name": "language",
+ "options": {
+ "JCR-SQL2": {
+ "label": "JCR-SQL2",
+ "value": "JCR-SQL2"
+ },
+ "xpath": {
+ "label": "xpath",
+ "value": "xpath"
+ },
+ "JCR-JQOM": {
+ "label": "JCR-JQOM",
+ "value": "JCR-JQOM"
+ },
+ "sql": {
+ "label": "SQL",
+ "value": "sql"
+ }
+ }
+ }
+ }
+ },
+ "querydebug": {
+ "jcr:primaryType": "nt:unstructured",
+ "sling:resourceType": "sling-cms/components/cms/querydebug"
+ }
+ }
+ }
+}
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/content/start.json
b/ui/src/main/resources/jcr_root/libs/sling-cms/content/start.json
index da14f8f..32bce47 100644
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/content/start.json
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/content/start.json
@@ -135,6 +135,12 @@
"link": "/cms/publication/home.html",
"text": "Publication"
},
+ "querydebug": {
+ "jcr:primaryType": "nt:unstructured",
+ "enabledGroups": ["administrators"],
+ "link": "/cms/admin/querydebug.html",
+ "text": "Query Debugger"
+ },
"systemconsole": {
"jcr:primaryType": "nt:unstructured",
"enabledGroups": ["administrators"],
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/i18n.json
b/ui/src/main/resources/jcr_root/libs/sling-cms/i18n.json
index 664c38e..83c871b 100644
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/i18n.json
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/i18n.json
@@ -4211,7 +4211,7 @@
"msg-job-name": {
"jcr:primaryType": "sling:MessageEntry",
"sling:message": "Name des Jobs",
- "sling:key": "Job Name"
+ "sling:key": "Job Name"
},
"msg-jpeg": {
"jcr:primaryType": "sling:MessageEntry",