{"id":8075,"date":"2014-12-11T05:55:04","date_gmt":"2014-12-11T13:55:04","guid":{"rendered":"http:\/\/localhost\/exoblog\/?p=8075"},"modified":"2023-06-05T16:49:46","modified_gmt":"2023-06-05T14:49:46","slug":"developing-juzu-portlets-step-5-saving-secrets-in-the-jcr","status":"publish","type":"post","link":"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-5-saving-secrets-in-the-jcr\/","title":{"rendered":"Developing Juzu portlets \u2013 Step 5: Saving Secrets in the JCR"},"content":{"rendered":"<p><a href=\"\/blog\/wp-content\/uploads\/2014\/12\/05-Juzu-tutorial-series.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8080\" src=\"\/blog\/wp-content\/uploads\/2014\/12\/05-Juzu-tutorial-series.jpg\" alt=\"05-Juzu-tutorial-series\" width=\"650\" height=\"220\" srcset=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2014\/12\/05-Juzu-tutorial-series.jpg 650w, https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2014\/12\/05-Juzu-tutorial-series-300x102.jpg 300w, https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2014\/12\/05-Juzu-tutorial-series-500x169.jpg 500w, https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2014\/12\/05-Juzu-tutorial-series-360x122.jpg 360w, https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2014\/12\/05-Juzu-tutorial-series-200x68.jpg 200w, https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2014\/12\/05-Juzu-tutorial-series-100x34.jpg 100w, https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2014\/12\/05-Juzu-tutorial-series-70x24.jpg 70w\" sizes=\"(max-width: 650px) 100vw, 650px\" \/><\/a><\/p>\n<p><em>Previous steps:<br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/learn-how-to-develop-great-juzu-portlets-for-exo-platform\">Learn how to develop great Juzu portlets for eXo Platform!<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-2-viewing-and-posting-secrets\">Step 2: viewing and posting secrets<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-3-building-a-sexy-secret-wall\">Step 3: Building a sexy secret wall<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-4-adding-likes-and-comments-for-secret\">Step 4: adding Likes and Comments for Secret<\/a><\/em><\/p>\n<p>Thanks to the <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-4-adding-likes-and-comments-for-secret\">previous steps<\/a>, we have a <strong>JuZcret application<\/strong> with some <strong>nice features<\/strong>. However, we\u2019re saving our secret in memory, which is good for testing but not for production. It\u2019s time to learn how to <strong>save our secret<\/strong> in <strong>eXo JCR<\/strong>. During this step, we will implement a new secret service to save all secrets in JCR instead of memory.<br \/>\n<!--more--><\/p>\n<h2>eXo services and eXo JCR storage<\/h2>\n<h3>JuZcret JCR NodeType declaration<\/h3>\n<p>First, we will create a <strong>JCR node type definition<\/strong>.<\/p>\n<p><em>Note: We will not focus on the eXo JCR API in this tutorial but on how to <strong>leverage JCR support from eXo Platform<\/strong> to develop Juzu Portlet. We will not explain in detail the JCR node type definition below. If you need more information to understand the code below, please take a look at the <a href=\"https:\/\/exojcr.jboss.org\/\" target=\"_blank\" rel=\"noopener\">eXo JCR website<\/a>.<\/em><\/p>\n<p>We will define the exo:secret and exo:secretComment node types. Their properties reflect our <strong>JuZcret domain classes<\/strong>: Secret and Comment.<\/p>\n<p>Create a new file <span class=\"navCode\">secret-nodetypes.xml<\/span> in <span class=\"navCode\">src\/main\/webapp\/WEB-INF\/conf<\/span>:<\/p>\n<pre class=\"lang:default decode:true \">&lt;nodeTypes xmlns:nt=\"https:\/\/www.jcp.org\/jcr\/nt\/1.0\" xmlns:mix=\"https:\/\/www.jcp.org\/jcr\/mix\/1.0\" xmlns:jcr=\"https:\/\/www.jcp.org\/jcr\/1.0\"&gt;\n\t&lt;nodeType name=\"exo:secret\" isMixin=\"false\" hasOrderableChildNodes=\"false\" primaryItemName=\"\"&gt;\n\t\t&lt;supertypes&gt;\n\t\t\t&lt;supertype&gt;nt:base&lt;\/supertype&gt;\n\t\t&lt;\/supertypes&gt;\n\t\t&lt;propertyDefinitions&gt;\n\t\t\t&lt;propertyDefinition name=\"exo:id\" requiredType=\"String\" autoCreated=\"false\" mandatory=\"true\" onParentVersion=\"COPY\" protected=\"false\" multiple=\"false\"&gt;\n\t\t\t\t&lt;valueConstraints\/&gt;\n\t\t\t&lt;\/propertyDefinition&gt;\n\t\t\t&lt;propertyDefinition name=\"exo:message\" requiredType=\"String\" autoCreated=\"false\" mandatory=\"true\" onParentVersion=\"COPY\" protected=\"false\" multiple=\"false\"&gt;\n\t\t\t\t&lt;valueConstraints\/&gt;\n\t\t\t&lt;\/propertyDefinition&gt;\n\t\t\t&lt;propertyDefinition name=\"exo:imageURL\" requiredType=\"String\" autoCreated=\"false\" mandatory=\"true\" onParentVersion=\"COPY\" protected=\"false\" multiple=\"false\"&gt;\n\t\t\t\t&lt;valueConstraints\/&gt;\n\t\t\t&lt;\/propertyDefinition&gt;\n\t\t\t&lt;propertyDefinition name=\"exo:likes\" requiredType=\"String\" autoCreated=\"false\" mandatory=\"false\" onParentVersion=\"COPY\" protected=\"false\" multiple=\"true\"&gt;\n\t\t\t\t&lt;valueConstraints\/&gt;\n\t\t\t&lt;\/propertyDefinition&gt;\n\t\t\t&lt;propertyDefinition name=\"exo:createdDate\" requiredType=\"Date\" autoCreated=\"false\" mandatory=\"true\" onParentVersion=\"COPY\" protected=\"false\" multiple=\"false\"&gt;\n\t\t\t\t&lt;valueConstraints\/&gt;\n\t\t\t&lt;\/propertyDefinition&gt;\n\t\t&lt;\/propertyDefinitions&gt;\n\t\t&lt;childNodeDefinitions&gt;\n\t\t\t&lt;childNodeDefinition name=\"*\" defaultPrimaryType=\"\" autoCreated=\"false\" mandatory=\"false\" onParentVersion=\"COPY\" protected=\"false\" sameNameSiblings=\"false\"&gt;\n\t\t\t\t&lt;requiredPrimaryTypes&gt;\n\t\t\t\t\t&lt;requiredPrimaryType&gt;exo:secretComment&lt;\/requiredPrimaryType&gt;\n\t\t\t\t&lt;\/requiredPrimaryTypes&gt;\n\t\t\t&lt;\/childNodeDefinition&gt;\n\t\t&lt;\/childNodeDefinitions&gt;\n\t&lt;\/nodeType&gt;\n\n\t&lt;nodeType  name=\"exo:secretComment\" isMixin=\"false\" hasOrderableChildNodes=\"false\" primaryItemName=\"\"&gt;\n\t\t&lt;supertypes&gt;\n\t\t\t&lt;supertype&gt;nt:base&lt;\/supertype&gt;\n\t\t&lt;\/supertypes&gt;\n\t\t&lt;propertyDefinitions&gt;\n\t\t\t&lt;propertyDefinition name=\"exo:id\" requiredType=\"String\" autoCreated=\"false\" mandatory=\"true\" onParentVersion=\"COPY\" protected=\"false\" multiple=\"false\"&gt;\n\t\t\t\t&lt;valueConstraints\/&gt;\n\t\t\t&lt;\/propertyDefinition&gt;\n\t\t\t&lt;propertyDefinition name=\"exo:userId\" requiredType=\"String\" autoCreated=\"false\" mandatory=\"true\" onParentVersion=\"COPY\" protected=\"false\" multiple=\"false\"&gt;\n\t\t\t\t&lt;valueConstraints\/&gt;\n\t\t\t&lt;\/propertyDefinition&gt;\n\t\t\t&lt;propertyDefinition name=\"exo:content\" requiredType=\"String\" autoCreated=\"false\" mandatory=\"true\" onParentVersion=\"COPY\" protected=\"false\" multiple=\"false\"&gt;\n\t\t\t\t&lt;valueConstraints\/&gt;\n\t\t\t&lt;\/propertyDefinition&gt;\n\t\t\t&lt;propertyDefinition name=\"exo:createdDate\" requiredType=\"Date\" autoCreated=\"false\" mandatory=\"true\" onParentVersion=\"COPY\" protected=\"false\" multiple=\"false\"&gt;\n\t\t\t\t&lt;valueConstraints\/&gt;\n\t\t\t&lt;\/propertyDefinition&gt;\n\t\t&lt;\/propertyDefinitions&gt;\n\t&lt;\/nodeType&gt;\n&lt;\/nodeTypes&gt;<\/pre>\n<p>When <span class=\"navCode\">secret-nodetypes.xml<\/span> file is ready, we need to register it to the <strong>eXo JCR service<\/strong>. Add this new eXo container <span class=\"navCode\">configuration.xml<\/span> file in <span class=\"navCode\">\/src\/main\/webapp\/WEB-INF\/conf\/<\/span>:<\/p>\n<pre class=\"lang:default decode:true \">&lt;configuration\n\txmlns:xsi=\"https:\/\/www.w3.org\/2001\/XMLSchema-instance\"\n\txsi:schemaLocation=\"https:\/\/www.exoplatform.org\/xml\/ns\/kernel_1_2.xsd https:\/\/www.exoplatform.org\/xml\/ns\/kernel_1_2.xsd\"\n\txmlns=\"https:\/\/www.exoplatform.org\/xml\/ns\/kernel_1_2.xsd\"&gt;\n\n\t&lt;external-component-plugins&gt;\n\t\t&lt;target-component&gt;org.exoplatform.services.jcr.RepositoryService&lt;\/target-component&gt;\n\t\t&lt;component-plugin&gt;\n\t\t\t&lt;name&gt;add.nodeType&lt;\/name&gt;\n\t\t\t&lt;set-method&gt;addPlugin&lt;\/set-method&gt;\n\t\t\t&lt;type&gt;org.exoplatform.services.jcr.impl.AddNodeTypePlugin&lt;\/type&gt;\n\t\t\t&lt;init-params&gt;\n\t\t\t\t&lt;values-param&gt;\n\t\t\t\t\t&lt;name&gt;autoCreatedInNewRepository&lt;\/name&gt;\n\t\t\t\t\t&lt;description&gt;Node types configuration file&lt;\/description&gt;\n\t\t\t\t\t&lt;value&gt;war:\/conf\/secret-nodetypes.xml&lt;\/value&gt;\n\t\t\t\t&lt;\/values-param&gt;\n\t\t\t&lt;\/init-params&gt;\n\t\t&lt;\/component-plugin&gt;\n\t&lt;\/external-component-plugins&gt;\n&lt;\/configuration&gt;\n<\/pre>\n<p>This configuration registers a <strong>node type plugin<\/strong> with eXo RepositoryService, which will parse our node type at war: <span class=\"navCode\">\/conf\/secret-nodetypes.xml<\/span> path.<\/p>\n<p>The only thing missing now is to make sure that eXo Platform will <strong>scan and process<\/strong> our <span class=\"navCode\">configuration.xml<\/span> file in <strong>tutorial-juzcret webapp<\/strong> when it initializes the eXo container. This task is not specific to Juzu; it\u2019s about configuring a webapp as an <strong>eXo Platform extension<\/strong> (for more details about extensions, please look at the eXo documentation).<\/p>\n<p>First, we need to modify the <span class=\"navCode\">web.xml<\/span> file:<\/p>\n<pre class=\"lang:default decode:true \">&lt;?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?&gt;\n&lt;web-app xmlns=\"https:\/\/java.sun.com\/xml\/ns\/javaee\"\n\txmlns:xsi=\"https:\/\/www.w3.org\/2001\/XMLSchema-instance\"\n\txsi:schemaLocation=\"https:\/\/java.sun.com\/xml\/ns\/javaee https:\/\/java.sun.com\/xml\/ns\/javaee\/web-app_3_0.xsd\"\n\tversion=\"3.0\"&gt;\n\n\t&lt;display-name&gt;tutorial-juzcret&lt;\/display-name&gt;\n\n\t&lt;!-- Run mode: prod, dev or live --&gt;\n\t&lt;context-param&gt;\n\t\t&lt;param-name&gt;juzu.run_mode&lt;\/param-name&gt;\n\t\t&lt;param-value&gt;${juzu.run_mode:dev}&lt;\/param-value&gt;\n\t&lt;\/context-param&gt;\n\n\t&lt;!-- Injection container to use: guice, spring, cdi or weld --&gt;\n\t&lt;context-param&gt;\n\t\t&lt;param-name&gt;juzu.inject&lt;\/param-name&gt;\n\t\t&lt;param-value&gt;guice&lt;\/param-value&gt;\n\t&lt;\/context-param&gt;\n\n\t&lt;listener&gt;\n\t\t&lt;listener-class&gt;org.exoplatform.container.web.PortalContainerConfigOwner&lt;\/listener-class&gt;\n\t&lt;\/listener&gt;\n&lt;\/web-app&gt;<\/pre>\n<p><strong>eXo container<\/strong> will need to know which webapp container contains its configuration files. By adding thePortalContainerConfigOwner, a <strong>servlet context listener<\/strong>, we\u2019ve registered the JuZcret webapp context to the eXo container to scan and process the xml configuration file. Notice that we also need to declare the <span class=\"navCode\">&lt;display-name&gt;<\/span> tag. The eXo container uses that information to <strong>map the registered webapp<\/strong>.<\/p>\n<p>Finally, we configure the JuZcret as a <strong>dependency of the eXo container<\/strong>. Even though we\u2019ve registered the webapp context, we still need to tell the eXo container that the JuZcret webapp is a <strong>portal container definition dependency<\/strong>. There are two places to add the configuration:<\/p>\n<ul>\n<li><span class=\"navCode\">TOMCAT\/gatein\/conf\/configuration.xml<\/span><\/li>\n<li>Create a jar file that contains <span class=\"navCode\">\/conf\/configuration.xml<\/span> and put it into <span class=\"navCode\">tomcat\/lib<\/span>.<\/li>\n<\/ul>\n<p><em>Note: We use the first solution in this tutorial, as it\u2019s quicker for the purpose of the tutorial, which is not about eXo Platform extension. For your next application, follow the official documentation and create a specific jar containing this configuration.xml file.<\/em><\/p>\n<p>Let\u2019s modify the <span class=\"navCode\">TOMCAT\/gatein\/conf\/configuration.xml<\/span> file:<\/p>\n<pre class=\"lang:default decode:true \">&lt;configuration\n\txmlns:xsi=\"https:\/\/www.w3.org\/2001\/XMLSchema-instance\"\n\txsi:schemaLocation=\"https:\/\/www.exoplatform.org\/xml\/ns\/kernel_1_2.xsd https:\/\/www.exoplatform.org\/xml\/ns\/kernel_1_2.xsd\"\n\txmlns=\"https:\/\/www.exoplatform.org\/xml\/ns\/kernel_1_2.xsd\"&gt;\n\n\t&lt;external-component-plugins&gt;\n\t\t&lt;target-component&gt;org.exoplatform.container.definition.PortalContainerConfig&lt;\/target-component&gt;\n\t\t&lt;component-plugin&gt;\n\t\t\t&lt;!-- The name of the plugin --&gt;\n\t\t\t&lt;name&gt;Change PortalContainer Definitions&lt;\/name&gt;\n\t\t\t&lt;!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions --&gt;\n\t\t\t&lt;set-method&gt;registerChangePlugin&lt;\/set-method&gt;\n\t\t\t&lt;!-- The full qualified name of the PortalContainerDefinitionChangePlugin --&gt;\n\t\t\t&lt;type&gt;org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin&lt;\/type&gt;\n\t\t\t&lt;init-params&gt;\n\t\t\t\t&lt;value-param&gt;\n\t\t\t\t\t&lt;name&gt;apply.default&lt;\/name&gt;\n\t\t\t\t\t&lt;value&gt;true&lt;\/value&gt;\n\t\t\t\t&lt;\/value-param&gt;\n\t\t\t\t&lt;object-param&gt;\n\t\t\t\t\t&lt;name&gt;change&lt;\/name&gt;\n\t\t\t\t\t&lt;object type=\"org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies\"&gt;\n\t\t\t\t\t\t&lt;!-- The list of name of the dependencies to add --&gt;\n\t\t\t\t\t\t&lt;field name=\"dependencies\"&gt;\n\t\t\t\t\t\t\t&lt;collection type=\"java.util.ArrayList\"&gt;\n\t\t\t\t\t\t\t\t&lt;value&gt;\n\t\t\t\t\t\t\t\t\t&lt;string&gt;tutorial-juzcret&lt;\/string&gt;\n\t\t\t\t\t\t\t\t&lt;\/value&gt;\n\t\t\t\t\t\t\t&lt;\/collection&gt;\n\t\t\t\t\t\t&lt;\/field&gt;\n\t\t\t\t\t&lt;\/object&gt;\n\t\t\t\t&lt;\/object-param&gt;\n\t\t\t&lt;\/init-params&gt;\n\t\t&lt;\/component-plugin&gt;\n\t&lt;\/external-component-plugins&gt;\n\n\t&lt;import&gt;jar:\/conf\/platform\/configuration.xml&lt;\/import&gt;\n\n&lt;\/configuration&gt;<\/pre>\n<p><em>Note: It\u2019s important to declare our application before the import of jar:\/conf\/platform\/configuration.xml.<\/em><\/p>\n<p>We finish declaring all necessary JCR node types for JuZcret, and we add the <strong>JuZcret webapp context as a portal container definition dependency<\/strong>.<\/p>\n<p>Now we can configure the <strong>eXo JCR service<\/strong>.<\/p>\n<h3>Binding the eXo JCR service<\/h3>\n<p>First, we need to declare dependency on the eXo kernel. Add <span class=\"navCode\">exo.jcr.component.ext<\/span> to the project <span class=\"navCode\">pom.xml<\/span>:<\/p>\n<pre class=\"lang:default decode:true \">&lt;dependency&gt;\n\t&lt;groupId&gt;org.exoplatform.jcr&lt;\/groupId&gt;\n\t&lt;artifactId&gt;exo.jcr.component.ext&lt;\/artifactId&gt;\n\t&lt;version&gt;1.15.x-SNAPSHOT&lt;\/version&gt;\n\t&lt;scope&gt;provided&lt;\/scope&gt;\n&lt;\/dependency&gt;<\/pre>\n<p>It\u2019s time for us to implement the <strong>new secret service<\/strong> with the <strong>JCR API<\/strong>. Let\u2019s create a new <span class=\"navCode\">SecretServiceJCRImpl.javaclass<\/span> in the <span class=\"navCode\">org.juzu.tutorial.services<\/span> package:<\/p>\n<pre class=\"lang:default decode:true \">package org.juzu.tutorial.services;\n\nimport org.exoplatform.services.jcr.ext.app.SessionProviderService;\nimport org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator;\nimport org.juzu.tutorial.models.Comment;\nimport org.juzu.tutorial.models.Secret;\n\nimport javax.inject.Inject;\nimport java.util.List;\nimport java.util.Set;\n\npublic class SecretServiceJCRImpl implements SecretService {\n\n    private static final String SECRET_APP = \"SecretApplication\";\n\n    private static final String CREATED_DATE = \"exo:createdDate\";\n\n    private static final String ID = \"exo:id\";\n\n    private static final String IMAGE_URL = \"exo:imageURL\";\n\n    private static final String LIKES = \"exo:likes\";\n\n    private static final String MESSAGE = \"exo:message\";\n\n    private static final String CONTENT = \"exo:content\";\n\n    private static final String USER_ID = \"exo:userId\";\n\n    private static final String SECRET_NODE_TYPE = \"exo:secret\";\n\n    private static final String COMMENT_NODE_TYPE = \"exo:secretComment\";\n\n    @Inject\n    private SessionProviderService  sessionService;\n\n    @Inject\n    private NodeHierarchyCreator    nodeCreator;\n\n    @Override\n    public List&lt;Secret&gt; getSecrets() {\n        return null;\n    }\n\n    @Override\n    public void addSecret(String message, String imageUrl) {\n\n    }\n\n    @Override\n    public Comment addComment(String secretId, Comment comment) {\n        return null;\n    }\n\n    @Override\n    public Set&lt;String&gt; addLike(String secretId, String userId) {\n        return null;\n    }\n}<\/pre>\n<p><strong>sessionService<\/strong> and <strong>nodeCreator<\/strong> are service components created by the eXo container. There must be a bridge between eXo Platform\u2019s container (the eXo container) and JuZcret\u2019s IOC container (Guice). This means that, before using it, we need to bind the necessary services in package-info.java:<\/p>\n<pre class=\"lang:default decode:true \">@Bindings({ @Binding(value = SecretService.class, implementation = SecretServiceJCRImpl.class),\n            @Binding(value = SessionProviderService.class),\n            @Binding(value = NodeHierarchyCreator.class)})\n\n[...]\n\nimport org.exoplatform.services.jcr.ext.app.SessionProviderService;\nimport org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator;\nimport org.juzu.tutorial.services.SecretService;\nimport org.juzu.tutorial.services.SecretServiceJCRImpl;<\/pre>\n<p>The <strong>secret service implementation<\/strong> is now the JCR version (we update the implementation of <span class=\"navCode\">SecretService.class<\/span> to <span class=\"navCode\">SecretServiceJCRImpl.class<\/span> instead of <span class=\"navCode\">SecretServiceMemImpl.class<\/span>).<\/p>\n<p>Notice that there is <strong>no declaration<\/strong> for the implementation class of SessionProviderService and NodeHierarchyCreator. We only declare the interfaces because <strong>JuZcret\u2019s IOC container<\/strong> will not instantiate those services itself but retrieve them from <strong>eXo\u2019s container<\/strong> by delegating the call to KernelProviderFactory (we declared this in step 4 using the service loader).<\/p>\n<h3>JCR service implementation<\/h3>\n<p>All necessary services are now managed by <strong>JuZcret\u2019s container<\/strong>, and they are ready to be injected and used.<\/p>\n<p>The NodeHierarchyCreator service will help us to initialize our JuZcret application\u2019s <strong>JCR data structure<\/strong>.<\/p>\n<p>We create the root node of the JuZcret application by adding this method to our secret service:<\/p>\n<pre class=\"lang:default decode:true \">import org.exoplatform.services.jcr.ext.common.SessionProvider;\nimport javax.jcr.Node;\n\npublic class SecretServiceJCRImpl implements SecretService {\n  [...]\n  private Node getSecretHome() throws Exception {\n    SessionProvider sProvider = sessionService.getSystemSessionProvider(null);\n    Node publicApp = nodeCreator.getPublicApplicationNode(sProvider);\n    try {\n      return publicApp.getNode(SECRET_APP);\n    } catch (Exception e) {\n      Node secretApp = publicApp.addNode(SECRET_APP, \"nt:unstructured\");\n      publicApp.getSession().save();\n      return secretApp;\n    }\n  }\n  [...]\n}<\/pre>\n<p>By calling the nodeCreator#getPublicApplicationNode method, we get the typical place to put <strong>application data<\/strong>. If it\u2019s the first time running JuZcret, we create a new SECRET_APP nt:unstructured node type. This node will then contain children with the node type exo:secret. This node type should reflect our Secret domain class, and then we must declare this to the <strong>eXo JCR service<\/strong> (we\u2019ll come back to this part later).<\/p>\n<p>Now we get the root application node; let\u2019s implement the function to <strong>add a new secret<\/strong>:<\/p>\n<pre class=\"lang:default decode:true \">[...]\n\nimport java.util.Calendar;\nimport java.util.UUID;\n\n[...]\n\npublic class SecretServiceJCRImpl implements SecretService {\n  \u2026\n\n  public void addSecret(String message, String imageUrl) {\n    String id = UUID.randomUUID().toString();\n    try {\n      Node secretHome = getSecretHome();\n      Node secret = secretHome.addNode(id, SECRET_NODE_TYPE);\n      secret.setProperty(ID, id);\n      secret.setProperty(MESSAGE, message);\n      secret.setProperty(IMAGE_URL, imageUrl);\n      secret.setProperty(CREATED_DATE, Calendar.getInstance());\n      secret.getSession().save();\n    } catch (Exception e) {\n      e.printStackTrace();\n    }\n  }\n\n  [...]\n}<\/pre>\n<p>It\u2019s a pretty simple <strong>JCR API<\/strong>, and actually, there is no Juzu stuff here. The most important thing here is getting the root node using the getSecretHome method.<\/p>\n<p>There are some other <strong>similar methods<\/strong> that we need to implement: <span class=\"navCode\">addComment<\/span>, <span class=\"navCode\">addLike<\/span>, and <span class=\"navCode\">getSecrets<\/span>:<\/p>\n<pre class=\"lang:default decode:true \">[...]\n\nimport java.util.HashSet\nimport java.util.LinkedList;\nimport javax.jcr.NodeIterator;\nimport javax.jcr.RepositoryException;\nimport javax.jcr.Value;\n\n[...]\n\n@Override\n  public List&lt;Secret&gt; getSecrets() {\n    List&lt;Secret&gt; secrets = new LinkedList&lt;Secret&gt;();\n    try {\n      Node secretHome = getSecretHome();\n      NodeIterator iterChild = secretHome.getNodes();\n      while (iterChild.hasNext()) {\n        secrets.add(buildSecret(iterChild.nextNode()));\n      }\n      return secrets;\n    } catch (Exception e) {\n      e.printStackTrace();\n      return null;\n    }\n  }\n\n  @Override\n  public Comment addComment(String secretId, Comment comment) {\n    String id = UUID.randomUUID().toString();\n\n    try {\n      Node secret = getSecretNode(secretId);\n\n      if (secret != null) {\n        Node cNode = secret.addNode(id, COMMENT_NODE_TYPE);\n        cNode.setProperty(ID, id);\n        cNode.setProperty(USER_ID, comment.getUserId());\n        cNode.setProperty(CONTENT, comment.getContent());\n        cNode.setProperty(CREATED_DATE, Calendar.getInstance());\n\n        cNode.getSession().save();\n        return buildComment(cNode);\n      }\n    } catch (Exception e) {\n      e.printStackTrace();\n    }\n    return null;\n  }\n\n  @Override\n  public Set&lt;String&gt; addLike(String secretId, String userId) {\n    try {\n      Node secret = getSecretNode(secretId);\n\n      if (secret != null) {\n        Set&lt;String&gt; likes = new HashSet&lt;String&gt;();\n        if (secret.hasProperty(LIKES)) {\n          Value[] values = secret.getProperty(LIKES).getValues();\n          for (Value v : values) {\n            likes.add(v.getString());\n          }\n        }\n        likes.add(userId);\n        secret.setProperty(LIKES, likes.toArray(new String[likes.size()]));\n\n        secret.save();\n        return likes;\n      }\n    } catch (Exception e) {\n      e.printStackTrace();\n    }\n    return null;\n }\n\n  private Node getSecretNode(String secretId) {\n    try {\n      Node secretHome = getSecretHome();\n      Node secret = secretHome.getNode(secretId);\n      return secret;\n    } catch (Exception e) {\n      e.printStackTrace();\n      return null;\n    }\n  }\n\nprivate Secret buildSecret(Node secretNode) throws RepositoryException {\n    Secret secret = new Secret();\n\n    List&lt;Comment&gt; comments = new LinkedList&lt;Comment&gt;();\n    NodeIterator commentIter = secretNode.getNodes();\n    while (commentIter.hasNext()) {\n      comments.add(buildComment(commentIter.nextNode()));\n    }\n    secret.setComments(comments);\n\n    secret.setCreatedDate(secretNode.getProperty(CREATED_DATE).getDate().getTime());\n    secret.setId(secretNode.getProperty(ID).getString());\n    secret.setImageURL(secretNode.getProperty(IMAGE_URL).getString());\n\n    Set&lt;String&gt; likes = new HashSet&lt;String&gt;();\n    if (secretNode.hasProperty(LIKES)) {\n      for (Value userID : secretNode.getProperty(LIKES).getValues()) {\n        likes.add(userID.getString());\n      }\n    }\n    secret.setLikes(likes);\n\n    secret.setMessage(secretNode.getProperty(MESSAGE).getString());\n    return secret;\n  }\n\n  private Comment buildComment(Node commentNode) throws RepositoryException {\n    Comment comment = new Comment();\n    comment.setContent(commentNode.getProperty(CONTENT).getString());\n    comment.setCreatedDate(commentNode.getProperty(CREATED_DATE).getDate().getTime());\n    comment.setId(commentNode.getProperty(ID).getString());\n    comment.setUserId(commentNode.getProperty(USER_ID).getString());\n    return comment;\n  }<\/pre>\n<p>That\u2019s all. The secret JCR service is now ready to use.<\/p>\n<p>Now re-compile and deploy JuZcret eXo Platform as explained in a previous step of this tutorial:<\/p>\n<pre class=\"lang:default decode:true \">$ mvn clean install<\/pre>\n<p>Copy\/paste the war <em>(replace the old one)<\/em> in the webapp folder of eXo Platform, start the server, and open the JuZcret page created in step 1.<\/p>\n<p>All features \u2013 sharing a secret, adding a comment, etc. \u2013 should run similarly to the previous memory service implementation, except that we <strong>don\u2019t lose a shared secret<\/strong> or comment after restarting the server. The data is now <strong>really persistent!<\/strong><\/p>\n<p>Next week, we will continue to improve our JuZcret application by adding internationalization.<\/p>\n<p><em>The final source of step 5 is available for <a href=\"https:\/\/github.com\/juzu\/portlet-tutorial\/tree\/step-5\/tutorial-juzcret\" target=\"_blank\" rel=\"noopener\">downloading on Github<\/a>.<\/em><\/p>\n<p><em>Previous and next steps:<br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/learn-how-to-develop-great-juzu-portlets-for-exo-platform\">Learn how to develop great Juzu portlets for eXo Platform!<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-2-viewing-and-posting-secrets\">Step 2: viewing and posting secrets<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-3-building-a-sexy-secret-wall\">Step 3: Building a sexy secret wall<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-4-adding-likes-and-comments-for-secret\">Step 4: adding Likes and Comments for Secret<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-5-saving-secrets-in-the-jcr\">Step 5: Saving Secrets in the JCR<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-6-let-the-whole-world-create-new-secrets\">Step 6: Let the whole world create new secrets<\/a><br \/>\n&#8211; <a href=\"https:\/\/www.exoplatform.com\/blog\/developing-juzu-portlets-step-7-finishing-the-job-properly-with-unit-test\">Step 7: Finishing the job properly with unit test<\/a><\/em><\/p>\n<p><b><a href=\"https:\/\/community.exoplatform.com\/portal\/dw\/\" target=\"_blank\" rel=\"noopener\">Join the eXo tribe<\/a> by registering for the community and get updates, tutorials, support, and access to the Platform and add-on downloads!<\/b><\/p>\n<p><!--begin adv-events--><\/p>\n<div class=\"adv-events\" style=\"background: #476fad; padding: 30px 20px; color: white;\">\n<div class=\"media\">\n<div class=\"pull-right\"><a href=\"#\"><br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6587 alignright\" src=\"https:\/\/www.exoplatform.com\/blog\/wp-content\/uploads\/2014\/02\/how-to-make-the-most-of-eXo-platform41.png\" alt=\"make-the-most-out-of-eXo-platform4\" width=\"161\" height=\"85\"><br \/>\n<\/a><\/div>\n<div class=\"media-body\">\n<h4 class=\"media-heading\">Make the most out of eXo Platform 4<\/h4>\n<p>Register to the next webinar and get a complete overview of what you can do with eXo Platform 4. <strong><a href=\"https:\/\/www.exoplatform.com\/contact-us\/\">Reserve your seat now!<\/a><\/strong><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!--end adv-events--><\/p>\n","protected":false},"excerpt":{"rendered":"Previous steps: &#8211; Learn how to develop great Juzu portlets for eXo Platform! &#8211; Step 2: viewing and posting secrets &#8211; Step 3: Building a sexy secret wall &#8211; Step 4: adding Likes and Comments for Secret Thanks to the previous steps, we have a JuZcret application with some nice features. However, we\u2019re saving our [&hellip;]","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[699],"tags":[],"lang":"en","translations":{"en":8075},"pll_sync_post":[],"_links":{"self":[{"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/posts\/8075"}],"collection":[{"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/comments?post=8075"}],"version-history":[{"count":0,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/posts\/8075\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/media?parent=8075"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/categories?post=8075"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.exoplatform.com\/blog\/wp-json\/wp\/v2\/tags?post=8075"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}