Java tutorial
/** * Copyright 2016-2016 Hippo B.V. (http://www.onehippo.com) * * Licensed 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.onehippo.forge.document.commenting.cms.impl; import java.text.MessageFormat; import java.util.Arrays; import java.util.Calendar; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.Set; import javax.jcr.ItemNotFoundException; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.query.Query; import javax.jcr.query.QueryResult; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DateFormatUtils; import org.hippoecm.frontend.plugin.config.IPluginConfig; import org.hippoecm.frontend.session.UserSession; import org.hippoecm.repository.util.JcrUtils; import org.onehippo.forge.document.commenting.cms.api.CommentItem; import org.onehippo.forge.document.commenting.cms.api.CommentPersistenceManager; import org.onehippo.forge.document.commenting.cms.api.CommentingContext; import org.onehippo.forge.document.commenting.cms.api.CommentingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link CommentPersistenceManager} implementation which stores data into JCR repository. */ public class DefaultJcrCommentPersistenceManager implements CommentPersistenceManager { private static final long serialVersionUID = 1L; private static Logger log = LoggerFactory.getLogger(DefaultJcrCommentPersistenceManager.class); private static final Object mutex = new Object(); public static final String DEFAULT_COMMENTS_LOCATION = "doccommentdata"; public static final String NT_COMMENTS_CONTAINER = "doccommenting:commentdatacontainer"; public static final String NT_COMMENT = "doccommenting:commentdata"; public static final String PROP_SUBJECTID = "doccommenting:subjectid"; public static final String PROP_AUTHOR = "doccommenting:author"; public static final String PROP_CREATED = "doccommenting:created"; public static final String PROP_LAST_MODIFIED = "doccommenting:lastModified"; public static final String PROP_CONTENT = "doccommenting:content"; private static final Set<String> BUILTIN_PROP_NAMES = new HashSet<>( Arrays.asList(PROP_SUBJECTID, PROP_AUTHOR, PROP_CREATED, PROP_LAST_MODIFIED, PROP_CONTENT)); private static final String DEFAULT_COMMENTS_QUERY = "//element(*,doccommenting:commentdata)[@doccommenting:subjectid=''{0}''] order by @doccommenting:created descending"; private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; private String dateFormat; public CommentItem getCommentItemById(CommentingContext commentingContext, String commentId) throws CommentingException { CommentItem commentItem = null; try { Node commentNode = getSession().getNodeByIdentifier(commentId); commentItem = new CommentItem(); mapCommentItem(commentingContext, commentItem, commentNode); } catch (RepositoryException e) { throw new CommentingException(e); } return commentItem; } public List<CommentItem> getLatestCommentItemsBySubjectId(CommentingContext commentingContext, String subjectId, long offset, long limit) throws CommentingException { List<CommentItem> commentItems = new LinkedList<>(); if (limit > 0) { try { Query query = createLatestCommentItemsReadQuery(commentingContext, subjectId, offset, limit); QueryResult result = query.execute(); Node commentNode; for (NodeIterator nodeIt = result.getNodes(); nodeIt.hasNext();) { commentNode = nodeIt.nextNode(); CommentItem commentItem = new CommentItem(); mapCommentItem(commentingContext, commentItem, commentNode); commentItems.add(commentItem); } } catch (RepositoryException e) { throw new CommentingException(e); } } return commentItems; } public String createCommentItem(CommentingContext commentingContext, CommentItem commentItem) throws CommentingException { String commentId = null; try { commentItem.setSubjectId( commentingContext.getSubjectDocumentModel().getNode().getParent().getIdentifier()); commentItem.setAuthor(getSession().getUserID()); Node docCommentsDataNode = getDocCommentsDataNode(getSession()); Node randomNode = createRandomNode(docCommentsDataNode); Node commentNode = randomNode.addNode("comment_" + System.currentTimeMillis(), NT_COMMENT); if (!commentNode.isNodeType("mix:referenceable")) { commentNode.addMixin("mix:referenceable"); } bindCommentNode(commentingContext, commentNode, commentItem); getSession().save(); commentId = commentNode.getIdentifier(); } catch (RepositoryException e1) { try { getSession().refresh(false); } catch (RepositoryException e2) { log.error("Failed to refresh.", e2); } throw new CommentingException(e1); } return commentId; } public void updateCommentItem(CommentingContext commentingContext, CommentItem commentItem) throws CommentingException { if (StringUtils.isBlank(commentItem.getId())) { throw new IllegalArgumentException("No identifier in commentItem."); } try { commentItem.setSubjectId( commentingContext.getSubjectDocumentModel().getNode().getParent().getIdentifier()); commentItem.setAuthor(getSession().getUserID()); Node commentNode = getSession().getNodeByIdentifier(commentItem.getId()); if (!commentNode.isNodeType("mix:referenceable")) { commentNode.addMixin("mix:referenceable"); } bindCommentNode(commentingContext, commentNode, commentItem); getSession().save(); } catch (RepositoryException e1) { try { getSession().refresh(false); } catch (RepositoryException e2) { log.error("Failed to refresh.", e2); } throw new CommentingException(e1); } } public void deleteCommentItem(CommentingContext commentingContext, CommentItem commentItem) throws CommentingException { try { Node commentNode = getSession().getNodeByIdentifier(commentItem.getId()); commentNode.remove(); getSession().save(); } catch (ItemNotFoundException e) { throw new CommentingException(e); } catch (RepositoryException e1) { try { getSession().refresh(false); } catch (RepositoryException e2) { log.error("Failed to refresh.", e2); } throw new CommentingException(e1); } } public String getCommentHeadText(CommentingContext commentingContext, CommentItem commentItem) throws CommentingException { StringBuilder sb = new StringBuilder(40); sb.append(getAuthorName(commentingContext, commentItem)).append(" - ") .append(DateFormatUtils.format(commentItem.getCreated(), getDateFormat(commentingContext))); return sb.toString(); } public String getCommentBodyText(CommentingContext commentingContext, CommentItem commentItem) throws CommentingException { return commentItem.getContent(); } public String getCommentHeadTooltip(CommentingContext commentingContext, CommentItem commentItem) throws CommentingException { return null; } public String getCommentBodyTooltip(CommentingContext commentingContext, CommentItem commentItem) throws CommentingException { return null; } protected Session getSession() { return UserSession.get().getJcrSession(); } protected Query createLatestCommentItemsReadQuery(CommentingContext commentingContext, String subjectId, long offset, long limit) throws RepositoryException { IPluginConfig config = commentingContext.getPluginConfig().getPluginConfig("cluster.options"); String queryTemplate = config.getString("jcr.comment.persistence.query", DEFAULT_COMMENTS_QUERY); String statement = MessageFormat.format(queryTemplate, subjectId); Query query = getSession().getWorkspace().getQueryManager().createQuery(statement, Query.XPATH); if (offset > 0L) { query.setOffset(offset); } if (limit > 0L) { query.setLimit(limit); } return query; } protected void mapCommentItem(CommentingContext commentingContext, final CommentItem commentItem, final Node commentNode) throws RepositoryException { commentItem.setId(commentNode.getIdentifier()); commentItem.setSubjectId(JcrUtils.getStringProperty(commentNode, PROP_SUBJECTID, "")); commentItem.setAuthor(JcrUtils.getStringProperty(commentNode, PROP_AUTHOR, "")); commentItem.setCreated(JcrUtils.getDateProperty(commentNode, PROP_CREATED, null)); commentItem.setLastModified(JcrUtils.getDateProperty(commentNode, PROP_LAST_MODIFIED, null)); commentItem.setContent(JcrUtils.getStringProperty(commentNode, PROP_CONTENT, "")); Property prop; String propName; int propType; for (PropertyIterator propIt = commentNode.getProperties(); propIt.hasNext();) { prop = propIt.nextProperty(); propName = prop.getName(); if (!BUILTIN_PROP_NAMES.contains(propName) && !prop.getDefinition().isProtected()) { propType = prop.getType(); if (!prop.isMultiple()) { if (propType == PropertyType.STRING) { commentItem.setAttribute(propName, prop.getString()); } else if (propType == PropertyType.BOOLEAN) { commentItem.setAttribute(propName, prop.getBoolean()); } else if (propType == PropertyType.LONG) { commentItem.setAttribute(propName, prop.getLong()); } else if (propType == PropertyType.DOUBLE) { commentItem.setAttribute(propName, prop.getDouble()); } else if (propType == PropertyType.DATE) { commentItem.setAttribute(propName, prop.getDate()); } } else { Value[] values = prop.getValues(); if (propType == PropertyType.STRING) { String[] stringValues = new String[values.length]; for (int i = 0; i < values.length; i++) { stringValues[i] = values[i].getString(); } commentItem.setAttribute(propName, stringValues); } else if (propType == PropertyType.BOOLEAN) { boolean[] booleanValues = new boolean[values.length]; for (int i = 0; i < values.length; i++) { booleanValues[i] = values[i].getBoolean(); } commentItem.setAttribute(propName, booleanValues); } else if (propType == PropertyType.LONG) { long[] longValues = new long[values.length]; for (int i = 0; i < values.length; i++) { longValues[i] = values[i].getLong(); } commentItem.setAttribute(propName, longValues); } else if (propType == PropertyType.DOUBLE) { double[] doubleValues = new double[values.length]; for (int i = 0; i < values.length; i++) { doubleValues[i] = values[i].getDouble(); } commentItem.setAttribute(propName, doubleValues); } else if (propType == PropertyType.DATE) { Calendar[] dateValues = new Calendar[values.length]; for (int i = 0; i < values.length; i++) { dateValues[i] = values[i].getDate(); } commentItem.setAttribute(propName, dateValues); } } } } } protected void bindCommentNode(CommentingContext commentingContext, final Node commentNode, final CommentItem commentItem) throws RepositoryException { commentNode.setProperty(PROP_SUBJECTID, StringUtils.defaultIfBlank(commentItem.getSubjectId(), "")); commentNode.setProperty(PROP_AUTHOR, StringUtils.defaultIfBlank(commentItem.getAuthor(), "")); Calendar now = Calendar.getInstance(); if (!commentNode.hasProperty(PROP_CREATED)) { commentNode.setProperty(PROP_CREATED, now); commentNode.setProperty(PROP_LAST_MODIFIED, now); } else { commentNode.setProperty(PROP_LAST_MODIFIED, now); } commentNode.setProperty(PROP_CONTENT, StringUtils.defaultIfBlank(commentItem.getContent(), "")); Property existingProp; Object value; for (String propName : commentItem.getAttributeNames()) { existingProp = null; if (commentNode.hasProperty(propName)) { existingProp = commentNode.getProperty(propName); } if (existingProp != null && existingProp.getDefinition().isProtected()) { continue; } value = commentItem.getAttribute(propName); if (value == null) { continue; } if (!value.getClass().isArray()) { if (value instanceof String) { commentNode.setProperty(propName, (String) value); } else if (value instanceof Boolean) { commentNode.setProperty(propName, (Boolean) value); } else if (value instanceof Long) { commentNode.setProperty(propName, (Long) value); } else if (value instanceof Double) { commentNode.setProperty(propName, (Double) value); } else if (value instanceof Calendar) { commentNode.setProperty(propName, (Calendar) value); } } else { if (value instanceof String[]) { String[] stringValues = (String[]) value; Value[] values = new Value[stringValues.length]; for (int i = 0; i < stringValues.length; i++) { values[i] = commentNode.getSession().getValueFactory().createValue(stringValues[i]); } commentNode.setProperty(propName, values); } else if (value instanceof boolean[]) { boolean[] booleanValues = (boolean[]) value; Value[] values = new Value[booleanValues.length]; for (int i = 0; i < booleanValues.length; i++) { values[i] = commentNode.getSession().getValueFactory().createValue(booleanValues[i]); } commentNode.setProperty(propName, values); } else if (value instanceof long[]) { long[] longValues = (long[]) value; Value[] values = new Value[longValues.length]; for (int i = 0; i < longValues.length; i++) { values[i] = commentNode.getSession().getValueFactory().createValue(longValues[i]); } commentNode.setProperty(propName, values); } else if (value instanceof double[]) { double[] doubleValues = (double[]) value; Value[] values = new Value[doubleValues.length]; for (int i = 0; i < doubleValues.length; i++) { values[i] = commentNode.getSession().getValueFactory().createValue(doubleValues[i]); } commentNode.setProperty(propName, values); } else if (value instanceof Calendar[]) { Calendar[] dateValues = (Calendar[]) value; Value[] values = new Value[dateValues.length]; for (int i = 0; i < dateValues.length; i++) { values[i] = commentNode.getSession().getValueFactory().createValue(dateValues[i]); } commentNode.setProperty(propName, values); } } } } protected String getDateFormat(final CommentingContext commentingContext) { if (dateFormat == null) { IPluginConfig config = commentingContext.getPluginConfig().getPluginConfig("cluster.options"); dateFormat = config.getString("jcr.comment.persistence.date.format", DEFAULT_DATE_FORMAT); } return dateFormat; } protected String getAuthorName(final CommentingContext commentingContext, final CommentItem commentItem) { return commentItem.getAuthor(); } private Node getDocCommentsDataNode(final Session session) throws RepositoryException { Node rootNode = session.getRootNode(); Node docCommentsDataNode; if (!rootNode.hasNode(DEFAULT_COMMENTS_LOCATION)) { synchronized (mutex) { docCommentsDataNode = rootNode.addNode(DEFAULT_COMMENTS_LOCATION, NT_COMMENTS_CONTAINER); addInitialStructure(docCommentsDataNode); } } else { docCommentsDataNode = rootNode.getNode(DEFAULT_COMMENTS_LOCATION); } return docCommentsDataNode; } private void addInitialStructure(Node commentData) throws RepositoryException { char a = 'a'; for (int i = 0; i < 26; i++) { Node letter = commentData.addNode(Character.toString((char) (a + i)), NT_COMMENTS_CONTAINER); for (int j = 0; j < 26; j++) { letter.addNode(Character.toString((char) (a + j)), NT_COMMENTS_CONTAINER); } } } private Node createRandomNode(Node docCommentsData) throws RepositoryException { Node result = docCommentsData; char a = 'a'; Random rand = new Random(); boolean needCheck = true; for (int i = 0; i < 4; i++) { int r = rand.nextInt(26); if (needCheck && result.hasNode(Character.toString((char) (a + r)))) { result = result.getNode(Character.toString((char) (a + r))); } else { needCheck = false; result = result.addNode(Character.toString((char) (a + r)), NT_COMMENTS_CONTAINER); } } return result; } }