Try now Demo en
  • en
  • fr
  • de
  • Product
    • Platform
      • Software TourFeatures & capabilities overview
      • Why eXoeXo Platform key differentiators
      • InternationalisationSupporting multilingual environments
      • MobileResponsive & available on any device
    • Technology
      • No CodeTailor eXo platform to your needs
      • ArchitectureAn overview of eXo Platform technology
      • IntegrationsAvailable connectors & integration capabilities
      • SecurityeXo Platform security measures
      • Open sourceComponents & licensing
  • Solutions
    • Communication
      • Modern IntranetBuild your company culture
      • Knowledge managementCentralize and share your company knowledge
      • Community managementEngage your community
      • ExtranetInvolve your clients and partners
    • Collaboration
      • Social NetworkConnect all your employees
      • Collaboration PlatformEmpower your teams
      • Employee PortalCentralize your work environment
      • Employee EngagementEngage & empower your employees
    • For
      • Public Sector
      • Networks
      • Education
      • Enterprises
  • Pricing
  • Resources
    • Resource center
      • Case studies
      • White Papers
      • Datasheets
      • Videos
    • Migration guide
      • Alternative to Microsoft 365
      • Alternative to Sharepoint
      • Alternative to Workplace from Meta
    • From The Blog
      • eXo Platform 6.5 is released: personalized navigation, multi-sites management and more
      • eXo launches its online community platform – eXo Tribe!
      • Cloud Vs On-premise Digital Workplace: Which one is right for your business?
  • Community
    • CommunityJoin our online community platform
    • DownloadLaunch eXo platform in your infrastructure
    • Source codeSource code on github
    • FAQsAbout the software, the community and our offers
    • REST APIs & DocumentationAll REST APIs available in eXo Platform
  • Company
    • Customers
    • Partners
    • Services
    • About us
    • Contact us
    • Newsroom
  • Menu mobile
    • Pricing
    • About us
    • Careers
    • Resource center
    • Blog
    • Contact us
    • Try eXo
Platform
  • Software Tour Features & capabilities overview
  • Why eXo eXo Platform key differentiators
  • Internationalisation Supporting multilingual environments
  • Mobile Responsive & available on any device
Technology
  • No Code Tailor eXo platform to your needs
  • Architecture An overview of eXo Platform technology
  • Integrations Available connectors & integration capabilities
  • Security eXo Platform security measures
  • Open source Components & licensing
Communication
  • Modern Intranet Build your company culture
  • Knowledge management Centralize and share your company knowledge
  • Community management Engage your community
  • Extranet Involve your clients and partners
Collaboration
  • Social Network Connect all your employees
  • Collaboration Platform Empower your teams
  • Employee Portal Centralize your work environment
  • Employee Engagement Engage & empower your employees
For
  • Public Sector
  • Networks
  • Education
  • Enterprises
Resource center
  • Case studies
  • White Papers
  • Datasheets
  • Videos
Migration guide
  • Alternative to Microsoft 365
  • Alternative to Sharepoint
  • Alternative to Workplace from Meta
From The Blog
  • eXo Platform 6.5 is released: personalized navigation, multi-sites management and more
  • eXo launches its online community platform – eXo Tribe!
  • Cloud Vs On-premise Digital Workplace: Which one is right for your business?
Community Join our online community platform
Download Launch eXo platform in your infrastructure
Source code Source code on github
FAQs About the software, the community and our offers
REST APIs & Documentation All REST APIs available in eXo Platform
Customers
Partners
Services
About us
Contact us
Newsroom
Pricing
About us
Careers
Resource center
Blog
Contact us
Try eXo
  1. Accueil
  2. Uncategorized
  3. Developing Juzu portlets – Step 5: Saving Secrets in the JCR

Developing Juzu portlets – Step 5: Saving Secrets in the JCR

05-Juzu-tutorial-series

Previous steps:
– Learn how to develop great Juzu portlets for eXo Platform!
– Step 2: viewing and posting secrets
– Step 3: Building a sexy secret wall
– Step 4: adding Likes and Comments for Secret

Thanks to the previous steps, we have a JuZcret application with some nice features. However, we’re saving our secret in memory, which is good for testing but not for production. It’s time to learn how to save our secret in eXo JCR. During this step, we will implement a new secret service to save all secrets in JCR instead of memory.

eXo services and eXo JCR storage

JuZcret JCR NodeType declaration

First, we will create a JCR node type definition.

Note: We will not focus on the eXo JCR API in this tutorial but on how to leverage JCR support from eXo Platform 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 eXo JCR website.

We will define the exo:secret and exo:secretComment node types. Their properties reflect our JuZcret domain classes: Secret and Comment.

Create a new file secret-nodetypes.xml in src/main/webapp/WEB-INF/conf:

<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">
	<nodeType name="exo:secret" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
		<supertypes>
			<supertype>nt:base</supertype>
		</supertypes>
		<propertyDefinitions>
			<propertyDefinition name="exo:id" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
				<valueConstraints/>
			</propertyDefinition>
			<propertyDefinition name="exo:message" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
				<valueConstraints/>
			</propertyDefinition>
			<propertyDefinition name="exo:imageURL" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
				<valueConstraints/>
			</propertyDefinition>
			<propertyDefinition name="exo:likes" requiredType="String" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="false" multiple="true">
				<valueConstraints/>
			</propertyDefinition>
			<propertyDefinition name="exo:createdDate" requiredType="Date" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
				<valueConstraints/>
			</propertyDefinition>
		</propertyDefinitions>
		<childNodeDefinitions>
			<childNodeDefinition name="*" defaultPrimaryType="" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="false" sameNameSiblings="false">
				<requiredPrimaryTypes>
					<requiredPrimaryType>exo:secretComment</requiredPrimaryType>
				</requiredPrimaryTypes>
			</childNodeDefinition>
		</childNodeDefinitions>
	</nodeType>

	<nodeType  name="exo:secretComment" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
		<supertypes>
			<supertype>nt:base</supertype>
		</supertypes>
		<propertyDefinitions>
			<propertyDefinition name="exo:id" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
				<valueConstraints/>
			</propertyDefinition>
			<propertyDefinition name="exo:userId" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
				<valueConstraints/>
			</propertyDefinition>
			<propertyDefinition name="exo:content" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
				<valueConstraints/>
			</propertyDefinition>
			<propertyDefinition name="exo:createdDate" requiredType="Date" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
				<valueConstraints/>
			</propertyDefinition>
		</propertyDefinitions>
	</nodeType>
</nodeTypes>

When secret-nodetypes.xml file is ready, we need to register it to the eXo JCR service. Add this new eXo container configuration.xml file in /src/main/webapp/WEB-INF/conf/:

<configuration
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://www.exoplatform.org/xml/ns/kernel_1_2.xsd https://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
	xmlns="https://www.exoplatform.org/xml/ns/kernel_1_2.xsd">

	<external-component-plugins>
		<target-component>org.exoplatform.services.jcr.RepositoryService</target-component>
		<component-plugin>
			<name>add.nodeType</name>
			<set-method>addPlugin</set-method>
			<type>org.exoplatform.services.jcr.impl.AddNodeTypePlugin</type>
			<init-params>
				<values-param>
					<name>autoCreatedInNewRepository</name>
					<description>Node types configuration file</description>
					<value>war:/conf/secret-nodetypes.xml</value>
				</values-param>
			</init-params>
		</component-plugin>
	</external-component-plugins>
</configuration>

This configuration registers a node type plugin with eXo RepositoryService, which will parse our node type at war: /conf/secret-nodetypes.xml path.

The only thing missing now is to make sure that eXo Platform will scan and process our configuration.xml file in tutorial-juzcret webapp when it initializes the eXo container. This task is not specific to Juzu; it’s about configuring a webapp as an eXo Platform extension (for more details about extensions, please look at the eXo documentation).

First, we need to modify the web.xml file:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<web-app xmlns="https://java.sun.com/xml/ns/javaee"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">

	<display-name>tutorial-juzcret</display-name>

	<!-- Run mode: prod, dev or live -->
	<context-param>
		<param-name>juzu.run_mode</param-name>
		<param-value>${juzu.run_mode:dev}</param-value>
	</context-param>

	<!-- Injection container to use: guice, spring, cdi or weld -->
	<context-param>
		<param-name>juzu.inject</param-name>
		<param-value>guice</param-value>
	</context-param>

	<listener>
		<listener-class>org.exoplatform.container.web.PortalContainerConfigOwner</listener-class>
	</listener>
</web-app>

eXo container will need to know which webapp container contains its configuration files. By adding thePortalContainerConfigOwner, a servlet context listener, we’ve 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 <display-name> tag. The eXo container uses that information to map the registered webapp.

Finally, we configure the JuZcret as a dependency of the eXo container. Even though we’ve registered the webapp context, we still need to tell the eXo container that the JuZcret webapp is a portal container definition dependency. There are two places to add the configuration:

  • TOMCAT/gatein/conf/configuration.xml
  • Create a jar file that contains /conf/configuration.xml and put it into tomcat/lib.

Note: We use the first solution in this tutorial, as it’s 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.

Let’s modify the TOMCAT/gatein/conf/configuration.xml file:

<configuration
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://www.exoplatform.org/xml/ns/kernel_1_2.xsd https://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
	xmlns="https://www.exoplatform.org/xml/ns/kernel_1_2.xsd">

	<external-component-plugins>
		<target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
		<component-plugin>
			<!-- The name of the plugin -->
			<name>Change PortalContainer Definitions</name>
			<!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
			<set-method>registerChangePlugin</set-method>
			<!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
			<type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
			<init-params>
				<value-param>
					<name>apply.default</name>
					<value>true</value>
				</value-param>
				<object-param>
					<name>change</name>
					<object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies">
						<!-- The list of name of the dependencies to add -->
						<field name="dependencies">
							<collection type="java.util.ArrayList">
								<value>
									<string>tutorial-juzcret</string>
								</value>
							</collection>
						</field>
					</object>
				</object-param>
			</init-params>
		</component-plugin>
	</external-component-plugins>

	<import>jar:/conf/platform/configuration.xml</import>

</configuration>

Note: It’s important to declare our application before the import of jar:/conf/platform/configuration.xml.

We finish declaring all necessary JCR node types for JuZcret, and we add the JuZcret webapp context as a portal container definition dependency.

Now we can configure the eXo JCR service.

Binding the eXo JCR service

First, we need to declare dependency on the eXo kernel. Add exo.jcr.component.ext to the project pom.xml:

<dependency>
	<groupId>org.exoplatform.jcr</groupId>
	<artifactId>exo.jcr.component.ext</artifactId>
	<version>1.15.x-SNAPSHOT</version>
	<scope>provided</scope>
</dependency>

It’s time for us to implement the new secret service with the JCR API. Let’s create a new SecretServiceJCRImpl.javaclass in the org.juzu.tutorial.services package:

package org.juzu.tutorial.services;

import org.exoplatform.services.jcr.ext.app.SessionProviderService;
import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator;
import org.juzu.tutorial.models.Comment;
import org.juzu.tutorial.models.Secret;

import javax.inject.Inject;
import java.util.List;
import java.util.Set;

public class SecretServiceJCRImpl implements SecretService {

    private static final String SECRET_APP = "SecretApplication";

    private static final String CREATED_DATE = "exo:createdDate";

    private static final String ID = "exo:id";

    private static final String IMAGE_URL = "exo:imageURL";

    private static final String LIKES = "exo:likes";

    private static final String MESSAGE = "exo:message";

    private static final String CONTENT = "exo:content";

    private static final String USER_ID = "exo:userId";

    private static final String SECRET_NODE_TYPE = "exo:secret";

    private static final String COMMENT_NODE_TYPE = "exo:secretComment";

    @Inject
    private SessionProviderService  sessionService;

    @Inject
    private NodeHierarchyCreator    nodeCreator;

    @Override
    public List<Secret> getSecrets() {
        return null;
    }

    @Override
    public void addSecret(String message, String imageUrl) {

    }

    @Override
    public Comment addComment(String secretId, Comment comment) {
        return null;
    }

    @Override
    public Set<String> addLike(String secretId, String userId) {
        return null;
    }
}

sessionService and nodeCreator are service components created by the eXo container. There must be a bridge between eXo Platform’s container (the eXo container) and JuZcret’s IOC container (Guice). This means that, before using it, we need to bind the necessary services in package-info.java:

@Bindings({ @Binding(value = SecretService.class, implementation = SecretServiceJCRImpl.class),
            @Binding(value = SessionProviderService.class),
            @Binding(value = NodeHierarchyCreator.class)})

[...]

import org.exoplatform.services.jcr.ext.app.SessionProviderService;
import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator;
import org.juzu.tutorial.services.SecretService;
import org.juzu.tutorial.services.SecretServiceJCRImpl;

The secret service implementation is now the JCR version (we update the implementation of SecretService.class to SecretServiceJCRImpl.class instead of SecretServiceMemImpl.class).

Notice that there is no declaration for the implementation class of SessionProviderService and NodeHierarchyCreator. We only declare the interfaces because JuZcret’s IOC container will not instantiate those services itself but retrieve them from eXo’s container by delegating the call to KernelProviderFactory (we declared this in step 4 using the service loader).

JCR service implementation

All necessary services are now managed by JuZcret’s container, and they are ready to be injected and used.

The NodeHierarchyCreator service will help us to initialize our JuZcret application’s JCR data structure.

We create the root node of the JuZcret application by adding this method to our secret service:

import org.exoplatform.services.jcr.ext.common.SessionProvider;
import javax.jcr.Node;

public class SecretServiceJCRImpl implements SecretService {
  [...]
  private Node getSecretHome() throws Exception {
    SessionProvider sProvider = sessionService.getSystemSessionProvider(null);
    Node publicApp = nodeCreator.getPublicApplicationNode(sProvider);
    try {
      return publicApp.getNode(SECRET_APP);
    } catch (Exception e) {
      Node secretApp = publicApp.addNode(SECRET_APP, "nt:unstructured");
      publicApp.getSession().save();
      return secretApp;
    }
  }
  [...]
}

By calling the nodeCreator#getPublicApplicationNode method, we get the typical place to put application data. If it’s 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 eXo JCR service (we’ll come back to this part later).

Now we get the root application node; let’s implement the function to add a new secret:

[...]

import java.util.Calendar;
import java.util.UUID;

[...]

public class SecretServiceJCRImpl implements SecretService {
  …

  public void addSecret(String message, String imageUrl) {
    String id = UUID.randomUUID().toString();
    try {
      Node secretHome = getSecretHome();
      Node secret = secretHome.addNode(id, SECRET_NODE_TYPE);
      secret.setProperty(ID, id);
      secret.setProperty(MESSAGE, message);
      secret.setProperty(IMAGE_URL, imageUrl);
      secret.setProperty(CREATED_DATE, Calendar.getInstance());
      secret.getSession().save();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  [...]
}

It’s a pretty simple JCR API, and actually, there is no Juzu stuff here. The most important thing here is getting the root node using the getSecretHome method.

There are some other similar methods that we need to implement: addComment, addLike, and getSecrets:

[...]

import java.util.HashSet
import java.util.LinkedList;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Value;

[...]

@Override
  public List<Secret> getSecrets() {
    List<Secret> secrets = new LinkedList<Secret>();
    try {
      Node secretHome = getSecretHome();
      NodeIterator iterChild = secretHome.getNodes();
      while (iterChild.hasNext()) {
        secrets.add(buildSecret(iterChild.nextNode()));
      }
      return secrets;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  @Override
  public Comment addComment(String secretId, Comment comment) {
    String id = UUID.randomUUID().toString();

    try {
      Node secret = getSecretNode(secretId);

      if (secret != null) {
        Node cNode = secret.addNode(id, COMMENT_NODE_TYPE);
        cNode.setProperty(ID, id);
        cNode.setProperty(USER_ID, comment.getUserId());
        cNode.setProperty(CONTENT, comment.getContent());
        cNode.setProperty(CREATED_DATE, Calendar.getInstance());

        cNode.getSession().save();
        return buildComment(cNode);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

  @Override
  public Set<String> addLike(String secretId, String userId) {
    try {
      Node secret = getSecretNode(secretId);

      if (secret != null) {
        Set<String> likes = new HashSet<String>();
        if (secret.hasProperty(LIKES)) {
          Value[] values = secret.getProperty(LIKES).getValues();
          for (Value v : values) {
            likes.add(v.getString());
          }
        }
        likes.add(userId);
        secret.setProperty(LIKES, likes.toArray(new String[likes.size()]));

        secret.save();
        return likes;
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
 }

  private Node getSecretNode(String secretId) {
    try {
      Node secretHome = getSecretHome();
      Node secret = secretHome.getNode(secretId);
      return secret;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

private Secret buildSecret(Node secretNode) throws RepositoryException {
    Secret secret = new Secret();

    List<Comment> comments = new LinkedList<Comment>();
    NodeIterator commentIter = secretNode.getNodes();
    while (commentIter.hasNext()) {
      comments.add(buildComment(commentIter.nextNode()));
    }
    secret.setComments(comments);

    secret.setCreatedDate(secretNode.getProperty(CREATED_DATE).getDate().getTime());
    secret.setId(secretNode.getProperty(ID).getString());
    secret.setImageURL(secretNode.getProperty(IMAGE_URL).getString());

    Set<String> likes = new HashSet<String>();
    if (secretNode.hasProperty(LIKES)) {
      for (Value userID : secretNode.getProperty(LIKES).getValues()) {
        likes.add(userID.getString());
      }
    }
    secret.setLikes(likes);

    secret.setMessage(secretNode.getProperty(MESSAGE).getString());
    return secret;
  }

  private Comment buildComment(Node commentNode) throws RepositoryException {
    Comment comment = new Comment();
    comment.setContent(commentNode.getProperty(CONTENT).getString());
    comment.setCreatedDate(commentNode.getProperty(CREATED_DATE).getDate().getTime());
    comment.setId(commentNode.getProperty(ID).getString());
    comment.setUserId(commentNode.getProperty(USER_ID).getString());
    return comment;
  }

That’s all. The secret JCR service is now ready to use.

Now re-compile and deploy JuZcret eXo Platform as explained in a previous step of this tutorial:

$ mvn clean install

Copy/paste the war (replace the old one) in the webapp folder of eXo Platform, start the server, and open the JuZcret page created in step 1.

All features – sharing a secret, adding a comment, etc. – should run similarly to the previous memory service implementation, except that we don’t lose a shared secret or comment after restarting the server. The data is now really persistent!

Next week, we will continue to improve our JuZcret application by adding internationalization.

The final source of step 5 is available for downloading on Github.

Previous and next steps:
– Learn how to develop great Juzu portlets for eXo Platform!
– Step 2: viewing and posting secrets
– Step 3: Building a sexy secret wall
– Step 4: adding Likes and Comments for Secret
– Step 5: Saving Secrets in the JCR
– Step 6: Let the whole world create new secrets
– Step 7: Finishing the job properly with unit test

Join the eXo tribe by registering for the community and get updates, tutorials, support, and access to the Platform and add-on downloads!


make-the-most-out-of-eXo-platform4

Make the most out of eXo Platform 4

Register to the next webinar and get a complete overview of what you can do with eXo Platform 4. Reserve your seat now!

Brahim Jaouane

I am a Digital Marketing specialist specialized in SEO at eXo Platform. Passionate about new technologies and Digital Marketing. With 10 years' experience, I support companies in their digital communication strategies and implement the tools necessary for their success. My approach combines the use of different traffic acquisition levers and an optimization of the user experience to convert visitors into customers. After various digital experiences in communication agencies as well as in B2B company, I have a wide range of skills and I am able to manage the digital marketing strategy of small and medium-sized companies.

Full-featured digital workplace with everything your employees need to work efficiently, smartly integrated for a compelling employee experience

  • Product
    • Software tour
    • Communication
    • Collaboration
    • Knowledge
    • Productivity
    • Open Source
    • Integrations
    • Security
  • Uses cases
    • Digital Workplace
    • Intranet software
    • Collaboration software
    • Knowledge management software
    • Entreprise Social Network
    • Employee Engagement platform
  • Roles
    • Internal Communications
    • Human Resources
    • Information Technology
  • Company
    • Product offer
    • Services Offer
    • Customers
    • Partners
    • About us
  • Resources
    • FAQs
    • Resource Center
    • Collaboration guide
    • What is a Digital workplace?
    • What is an intranet?
    • Employee engagement
  • Terms and Conditions
  • Legal
  • Privacy Policy
  • Accessibility
  • Contact us
  • Sitemap
  • Facebook
  • Twitter
  • LinkedIn
wpDiscuz