Java tutorial
/** * ========================================================================================== * = JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION = * ========================================================================================== * * http://www.jahia.com * * Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved. * * THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES: * 1/GPL OR 2/JSEL * * 1/ GPL * ================================================================================== * * IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * * 2/ JSEL - Commercial and Supported Versions of the program * =================================================================================== * * IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * Alternatively, commercial and supported versions of the program - also known as * Enterprise Distributions - must be used in accordance with the terms and conditions * contained in a separate written agreement between you and Jahia Solutions Group SA. * * If you are unsure which license is appropriate for your use, * please contact the sales department at sales@jahia.com. */ package org.jahia.services.seo.jcr; import static org.jahia.api.Constants.JCR_LANGUAGE; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.query.Query; import javax.jcr.query.QueryResult; import javax.validation.ConstraintViolationException; import org.apache.commons.collections.KeyValue; import org.apache.commons.collections.keyvalue.DefaultKeyValue; import org.apache.commons.lang.StringUtils; import org.jahia.api.Constants; import org.jahia.services.content.JCRCallback; import org.jahia.services.content.JCRContentUtils; import org.jahia.services.content.JCRNodeWrapper; import org.jahia.services.content.JCRSessionWrapper; import org.jahia.services.content.JCRTemplate; import org.jahia.services.seo.VanityUrl; /** * Manager for vanity URLs in Jahia * * @author Benjamin Papez */ public class VanityUrlManager { /** mixin, which will automatically be attached once a vanity URL mapping is applied to a node */ public static final String JAHIAMIX_VANITYURLMAPPED = "jmix:vanityUrlMapped"; /** node type of the vanity URL node */ public static final String JAHIANT_VANITYURL = "jnt:vanityUrl"; public static final String JAHIANT_VANITYURLS = "jnt:vanityUrls"; /** node name holding vanity URL mappings for a node */ public static final String VANITYURLMAPPINGS_NODE = "vanityUrlMapping"; /** Property name holding the vanity URL */ public static final String PROPERTY_URL = "j:url"; /** Property name specifying if vanity URL is the default one for a locale */ public static final String PROPERTY_DEFAULT = "j:default"; /** Property name specifying if vanity URL is active */ public static final String PROPERTY_ACTIVE = "j:active"; /** * Find any mappings for the given vanity URL. If a site is specified the * query will be done only for the specified site, otherwise all sites in the * workspace will be searched through and a list of VanityURL beans will be * returned. The method searches mappings in any language. * * @param url URL path to check whether there is a content mapping for it (URL must start with /) * @param site key of the site to search for the mapping or all sites if the string is null or empty * @param session the JCR session holding the information about workspace and locale * @return the list of VanityUrl beans * @throws RepositoryException if there was an unexpected exception accessing the repository */ @SuppressWarnings("deprecation") public List<VanityUrl> findExistingVanityUrls(String url, String site, JCRSessionWrapper session) throws RepositoryException { QueryResult result = session.getWorkspace().getQueryManager() .createQuery( "/jcr:root" + (StringUtils.isEmpty(site) ? "" : "/sites/" + JCRContentUtils.stringToJCRPathExp(site)) + "//element(*, " + JAHIANT_VANITYURL + ")[@" + PROPERTY_URL + " = " + JCRContentUtils.stringToQueryLiteral(url) + ("live".equals(session.getWorkspace()) ? " and @j:published = 'true'" : "") + "]", Query.XPATH) .execute(); List<VanityUrl> existingUrls = new ArrayList<VanityUrl>(); for (NodeIterator it = result.getNodes(); it.hasNext();) { JCRNodeWrapper node = (JCRNodeWrapper) it.next(); existingUrls.add(populateJCRData(node, new VanityUrl())); } return existingUrls; } /** * Gets a node's default vanity URL for the current locale or if none is default * then take the first mapping for the locale. * * @param contentNode the content node for which to return a mapping * @param siteKey * @param session the JCR session holding the information about workspace and locale * @return the VanityUrl bean * @throws RepositoryException if there was an unexpected exception accessing the repository */ public VanityUrl getVanityUrlForCurrentLocale(JCRNodeWrapper contentNode, String siteKey, JCRSessionWrapper session) throws RepositoryException { VanityUrl vanityUrl = null; if (contentNode.isNodeType(JAHIAMIX_VANITYURLMAPPED) && contentNode.hasNode(VANITYURLMAPPINGS_NODE)) { boolean isSite = (contentNode.getResolveSite().getSiteKey().equals(siteKey)); String currentLanguage = session.getLocale().toString(); for (NodeIterator it = session.getNode(contentNode.getPath() + "/" + VANITYURLMAPPINGS_NODE) .getNodes(); it.hasNext();) { JCRNodeWrapper currentNode = (JCRNodeWrapper) it.next(); if (currentNode.getPropertyAsString(JCR_LANGUAGE).equals(currentLanguage)) { if (currentNode.getProperty(PROPERTY_DEFAULT).getBoolean() && isSite) { vanityUrl = populateJCRData(currentNode, new VanityUrl()); } if (vanityUrl != null) { break; } } } } return vanityUrl; } /** * Gets all node's vanity URLs for the given locale * * @param contentNode the content node for which to return the mappings * @param languageCode the language code for which to return the mappings * @param session the JCR session holding the information about the workspace and user * @return the list of VanityUrl beans * @throws RepositoryException if there was an unexpected exception accessing the repository */ public List<VanityUrl> getVanityUrls(JCRNodeWrapper contentNode, String languageCode, JCRSessionWrapper session) throws RepositoryException { List<VanityUrl> vanityUrls = new ArrayList<VanityUrl>(); contentNode = session.getNodeByUUID(contentNode.getIdentifier()); if (contentNode.isNodeType(JAHIAMIX_VANITYURLMAPPED) && contentNode.hasNode(VANITYURLMAPPINGS_NODE)) { for (NodeIterator it = contentNode.getNode(VANITYURLMAPPINGS_NODE).getNodes(); it.hasNext();) { JCRNodeWrapper currentNode = (JCRNodeWrapper) it.next(); if (currentNode.getPropertyAsString(JCR_LANGUAGE).equals(languageCode)) { vanityUrls.add(populateJCRData(currentNode, new VanityUrl())); } } } return vanityUrls; } /** * Completely delete a mapped vanity URL. If the deleted vanity URL is the * default one for the current locale, then check whether there are other * active mappings for the same locale and set the first found one as the * new default. * * @param contentNode the content node for which to remove the given mapping * @param vanityUrl the VanityUrl bean representing the URL to be removed * @param session the JCR session used to find and remove the vanity URL node * @return true if the vanity URL was removed or false if it was not removed * @throws RepositoryException if there was an unexpected exception accessing the repository */ public boolean removeVanityUrlMapping(JCRNodeWrapper contentNode, VanityUrl vanityUrl, JCRSessionWrapper session) throws RepositoryException { JCRNodeWrapper vanityUrlNode = null; JCRNodeWrapper newDefaultVanityUrlNode = null; boolean found = false; JCRNodeWrapper vanityUrlMappingsNode = null; if (contentNode.isNodeType(JAHIAMIX_VANITYURLMAPPED) && contentNode.hasNode(VANITYURLMAPPINGS_NODE)) { vanityUrlMappingsNode = contentNode.getNode(VANITYURLMAPPINGS_NODE); for (NodeIterator it = vanityUrlMappingsNode.getNodes(); it.hasNext();) { JCRNodeWrapper currentNode = (JCRNodeWrapper) it.next(); if (vanityUrl.isDefaultMapping() && currentNode.getPropertyAsString(JCR_LANGUAGE).equals(vanityUrl.getLanguage()) && currentNode.getProperty(PROPERTY_ACTIVE).getBoolean() && !currentNode.getPropertyAsString(PROPERTY_URL).equals(vanityUrl.getUrl())) { newDefaultVanityUrlNode = currentNode; if (found) { break; } } if (currentNode.getPropertyAsString(PROPERTY_URL).equals(vanityUrl.getUrl())) { vanityUrlNode = currentNode; if (!vanityUrl.isDefaultMapping() || newDefaultVanityUrlNode != null) { found = true; break; } } } } if (vanityUrlNode != null) { // does not exist yet session.checkout(vanityUrlMappingsNode); vanityUrlNode.remove(); if (newDefaultVanityUrlNode != null) { newDefaultVanityUrlNode.setProperty(PROPERTY_DEFAULT, true); } session.save(); return true; } else { return false; } } /** * Completely delete all mapped vanity URL for a locale. * * @param contentNode the content node for which to remove the mappings * @param languageCode the language code for which the mappings should be removed * @param session the JCR session used to find and remove the vanity URL nodes * @return true if the vanity URLs were removed or false if not * @throws RepositoryException if there was an unexpected exception accessing the repository */ public boolean removeVanityUrlMappings(JCRNodeWrapper contentNode, String languageCode, JCRSessionWrapper session) throws RepositoryException { if (contentNode.isNodeType(JAHIAMIX_VANITYURLMAPPED) && contentNode.hasNode(VANITYURLMAPPINGS_NODE)) { JCRNodeWrapper vanityUrlMappingsNode = contentNode.getNode(VANITYURLMAPPINGS_NODE); NodeIterator it = vanityUrlMappingsNode.getNodes(); if (it.hasNext()) { List<Node> toRemove = new LinkedList<Node>(); for (; it.hasNext();) { Node node = it.nextNode(); if (languageCode.equals(node.getProperty(JCR_LANGUAGE).getString())) { toRemove.add(node); } } if (!toRemove.isEmpty()) { session.checkout(vanityUrlMappingsNode); for (Node node : toRemove) { node.remove(); } session.save(); return true; } } } return false; } /** * Add or update a vanity URL mapping for a specific content node and the language code set in the * VanityUrl bean. * * If the URL mapping has already been saved before we check whether the default and active flag in * the bean is different to the saved one, so we do an update, otherwise no operation is done. * * If the new or updated mapping is now the default one for the language, then we also check * if there already is another default URL for this language and set its default flag to false. * * We also check whether the same URL is already existing for a different node or language * in the current site and throw a ConstraintViolationException if this is the case. * * @param contentNode the content node for which to add the given mapping * @param vanityUrl the VanityUrl bean representing the URL to be added * @param session the JCR session used to find and save the vanity URL node * @return true if the vanity URL was added or false if it was not added * @throws ConstraintViolationException if the vanity URL mapping already exists for a different content node or language in the site * @throws RepositoryException if there was an unexpected exception accessing the repository */ public boolean saveVanityUrlMapping(JCRNodeWrapper contentNode, VanityUrl vanityUrl, JCRSessionWrapper session) throws RepositoryException { checkUniqueConstraint(contentNode, vanityUrl, null); JCRNodeWrapper vanityUrlNode = null; JCRNodeWrapper previousDefaultVanityUrlNode = null; JCRNodeWrapper vanityUrlMappingsNode = null; if (!contentNode.hasNode(VANITYURLMAPPINGS_NODE)) { if (!contentNode.isNodeType(JAHIAMIX_VANITYURLMAPPED)) { contentNode.addMixin(JAHIAMIX_VANITYURLMAPPED); } vanityUrlMappingsNode = contentNode.addNode(VANITYURLMAPPINGS_NODE, JAHIANT_VANITYURLS); } else { vanityUrlMappingsNode = contentNode.getNode(VANITYURLMAPPINGS_NODE); boolean found = false; for (NodeIterator it = vanityUrlMappingsNode.getNodes(); it.hasNext();) { JCRNodeWrapper currentNode = (JCRNodeWrapper) it.next(); if (vanityUrl.isDefaultMapping() && currentNode.getPropertyAsString(JCR_LANGUAGE).equals(vanityUrl.getLanguage()) && currentNode.getProperty(PROPERTY_DEFAULT).getBoolean()) { previousDefaultVanityUrlNode = currentNode; if (found) { break; } } if (currentNode.getPropertyAsString(PROPERTY_URL).equals(vanityUrl.getUrl())) { vanityUrlNode = currentNode; if (!vanityUrl.isDefaultMapping() || previousDefaultVanityUrlNode != null) { found = true; break; } } } } if (vanityUrlNode == null) { // does not exist yet session.checkout(vanityUrlMappingsNode); vanityUrlNode = vanityUrlMappingsNode.addNode(JCRContentUtils.escapeLocalNodeName(vanityUrl.getUrl()), JAHIANT_VANITYURL); } else if (vanityUrlNode.getProperty(PROPERTY_ACTIVE).getBoolean() == vanityUrl.isActive() && vanityUrlNode.getProperty(PROPERTY_DEFAULT).getBoolean() == vanityUrl.isDefaultMapping()) { return false; } else { // node(s) will be updated session.checkout(vanityUrlMappingsNode); } vanityUrlNode.setProperty(PROPERTY_URL, vanityUrl.getUrl()); vanityUrlNode.setProperty(JCR_LANGUAGE, vanityUrl.getLanguage()); vanityUrlNode.setProperty(PROPERTY_ACTIVE, vanityUrl.isActive()); vanityUrlNode.setProperty(PROPERTY_DEFAULT, vanityUrl.isDefaultMapping()); if (previousDefaultVanityUrlNode != null) { previousDefaultVanityUrlNode.setProperty(PROPERTY_DEFAULT, false); } session.save(); return true; } /** * Add, update or delete all vanity URL mappings for a specific content node and the language codes * set in the list of VanityUrl beans. First we load all existing mappings for all the languages * set in the updatedLocales collection. Then we compare the existing with the list of URL mappings * given in the vanityUrls collection to know which nodes need to be added, updated or deleted. * * If the default mapping for a language is set for a new or updated mapping, then we check if * there already is another default URL for this language and set its default flag to false. * * We also check whether the same added URL is already existing for a different node or language * in the current site (and which is not being deleted in the same operation) and throw a * ConstraintViolationException if this is the case. We also throw a ConstraintViolationException * if two URL mappings for the same language in the given vanityUrls collection have the default flag * set to true. * * @param contentNode the content node for which to add the given mapping * @param vanityUrls the list of VanityUrls bean representing the URLs to be added or updated * @param updatedLocales a set with all locales, which have been edited (e.g. if all mappings for a language * need to be deleted, add the language to this set, while in the vanityUrls list there will be no * mappings for that language) * @param session the JCR session used to find and save the vanity URL nodes * @return true if any vanity URL was added,updated or deleted or false if no change was done * @throws ConstraintViolationException if the vanity URL mapping already exists for a different content node or language in the site * @throws RepositoryException if there was an unexpected exception accessing the repository */ public boolean saveVanityUrlMappings(JCRNodeWrapper contentNode, List<VanityUrl> vanityUrls, final Set<String> updatedLocales, JCRSessionWrapper session) throws RepositoryException { Map<String, Map<String, VanityUrl>> existingMappings = new HashMap<String, Map<String, VanityUrl>>(); Map<String, KeyValue> oldDefaultMappings = new HashMap<String, KeyValue>(); JCRNodeWrapper vanityUrlMappingsNode = null; if (!contentNode.hasNode(VANITYURLMAPPINGS_NODE)) { if (!contentNode.isNodeType(JAHIAMIX_VANITYURLMAPPED)) { contentNode.addMixin(JAHIAMIX_VANITYURLMAPPED); } vanityUrlMappingsNode = contentNode.addNode(VANITYURLMAPPINGS_NODE, JAHIANT_VANITYURLS); } else { vanityUrlMappingsNode = contentNode.getNode(VANITYURLMAPPINGS_NODE); // first we get all existing mappings and find the old default mappings per language for (NodeIterator it = vanityUrlMappingsNode.getNodes(); it.hasNext();) { JCRNodeWrapper currentNode = (JCRNodeWrapper) it.next(); String language = currentNode.getPropertyAsString(JCR_LANGUAGE); if (updatedLocales.contains(language)) { Map<String, VanityUrl> existingVanityUrls = existingMappings.get(language); if (existingVanityUrls == null) { existingMappings.put(language, new HashMap<String, VanityUrl>()); } VanityUrl vanityUrl = populateJCRData(currentNode, new VanityUrl()); existingMappings.get(language).put(currentNode.getName(), vanityUrl); if (currentNode.getProperty(PROPERTY_DEFAULT).getBoolean()) { oldDefaultMappings.put(language, new DefaultKeyValue(currentNode.getName(), vanityUrl)); } } } } // as next we need to find out, which mappings need to be updated or added and // get the collection of new default mappings. List<VanityUrl> toAdd = new ArrayList<VanityUrl>(); Map<String, VanityUrl> toUpdate = new HashMap<String, VanityUrl>(); Map<String, VanityUrl> newDefaultMappings = new HashMap<String, VanityUrl>(); for (VanityUrl vanityUrl : vanityUrls) { if (vanityUrl.isDefaultMapping()) { VanityUrl otherDefaultMapping = newDefaultMappings.put(vanityUrl.getLanguage(), vanityUrl); if (otherDefaultMapping != null) { throw new ConstraintViolationException( "Two mappings are set as default for the same language: " + vanityUrl.getUrl() + " and " + otherDefaultMapping.getUrl() + " for language: " + vanityUrl.getLanguage(), null); } } boolean found = false; Map<String, VanityUrl> mappings = existingMappings.get(vanityUrl.getLanguage()); if (mappings != null) { for (Map.Entry<String, VanityUrl> entry : mappings.entrySet()) { if (entry.getValue().equals(vanityUrl)) { mappings.remove(entry.getKey()); found = true; if (entry.getValue().isActive() != vanityUrl.isActive() || entry.getValue().isDefaultMapping() != vanityUrl.isDefaultMapping()) { vanityUrl.setIdentifier(entry.getValue().getIdentifier()); toUpdate.put(entry.getKey(), vanityUrl); } break; } } } if (!found && updatedLocales.contains(vanityUrl.getLanguage())) { toAdd.add(vanityUrl); } } // Compare the new default settings with the old ones to see which mapping should // be default. Also consider the case, that in the new collection none is set to // default, then we take the previous default one or if there was also no default, // then the first found mapping for a language will be default. if (!newDefaultMappings.keySet().containsAll(updatedLocales)) { for (String locale : updatedLocales) { if (!newDefaultMappings.containsKey(locale)) { boolean defaultWasSet = false; VanityUrl oldDefaultVanityUrl = null; if (oldDefaultMappings.get(locale) != null) { oldDefaultVanityUrl = (VanityUrl) oldDefaultMappings.get(locale).getValue(); for (Map.Entry<String, VanityUrl> entry : toUpdate.entrySet()) { VanityUrl vanityUrl = entry.getValue(); if (vanityUrl.equals(oldDefaultVanityUrl)) { vanityUrl.setDefaultMapping(true); newDefaultMappings.put(locale, vanityUrl); defaultWasSet = true; } } } if (!defaultWasSet) { for (VanityUrl vanityUrl : vanityUrls) { if (locale.equals(vanityUrl.getLanguage())) { vanityUrl.setDefaultMapping(true); newDefaultMappings.put(locale, vanityUrl); break; } } } } } } // At last we need to see, which mappings are no longer existing in the new collection, // which means that they need to be completely removed List<Map.Entry<String, VanityUrl>> toDelete = new ArrayList<Map.Entry<String, VanityUrl>>(); for (Map<String, VanityUrl> existingVanityUrls : existingMappings.values()) { toDelete.addAll(existingVanityUrls.entrySet()); } // Compare the new default settings with the old ones to know if the default flag needs // to be set to false for the previous default. List<String> removeDefaultMapping = new ArrayList<String>(); for (Map.Entry<String, KeyValue> oldDefaultMapping : oldDefaultMappings.entrySet()) { VanityUrl oldDefaultVanityUrl = (VanityUrl) oldDefaultMapping.getValue().getValue(); VanityUrl newDefaultVanityUrl = newDefaultMappings.get(oldDefaultMapping.getKey()); if (!oldDefaultVanityUrl.equals(newDefaultVanityUrl)) { boolean oldDefaultWillBeDeleted = false; for (Map.Entry<String, VanityUrl> entry : toDelete) { if (oldDefaultVanityUrl.equals(entry.getValue())) { oldDefaultWillBeDeleted = true; break; } } if (!(oldDefaultWillBeDeleted || toUpdate.values().contains(oldDefaultVanityUrl))) { removeDefaultMapping.add((String) oldDefaultMapping.getValue().getKey()); } } } // Check if the added vanity URLs are really unique for the site for (VanityUrl vanityUrl : toAdd) { checkUniqueConstraint(contentNode, vanityUrl, toDelete); } // If there is no change do nothing otherwise do all the operations and // save the session if (toUpdate.isEmpty() && toAdd.isEmpty() && toDelete.isEmpty()) { return false; } else { session.checkout(vanityUrlMappingsNode); for (Map.Entry<String, VanityUrl> entry : toUpdate.entrySet()) { JCRNodeWrapper vanityUrlNode = vanityUrlMappingsNode.getNode(entry.getKey()); VanityUrl vanityUrl = entry.getValue(); session.checkout(vanityUrlNode); vanityUrlNode.setProperty(PROPERTY_ACTIVE, vanityUrl.isActive()); vanityUrlNode.setProperty(PROPERTY_DEFAULT, vanityUrl.isDefaultMapping()); } for (String index : removeDefaultMapping) { JCRNodeWrapper vanityUrlNode = vanityUrlMappingsNode.getNode(index); session.checkout(vanityUrlNode); vanityUrlNode.setProperty(PROPERTY_DEFAULT, false); } for (Map.Entry<String, VanityUrl> entry : toDelete) { JCRNodeWrapper vanityUrlNode = vanityUrlMappingsNode.getNode(entry.getKey()); session.checkout(vanityUrlNode); vanityUrlNode.remove(); } for (VanityUrl vanityUrl : toAdd) { JCRNodeWrapper vanityUrlNode = vanityUrlMappingsNode .addNode(JCRContentUtils.escapeLocalNodeName(vanityUrl.getUrl()), JAHIANT_VANITYURL); session.checkout(vanityUrlNode); vanityUrlNode.setProperty(PROPERTY_URL, vanityUrl.getUrl()); vanityUrlNode.setProperty(JCR_LANGUAGE, vanityUrl.getLanguage()); vanityUrlNode.setProperty(PROPERTY_ACTIVE, vanityUrl.isActive()); vanityUrlNode.setProperty(PROPERTY_DEFAULT, vanityUrl.isDefaultMapping()); } session.save(); } return true; } private void checkUniqueConstraint(final JCRNodeWrapper contentNode, final VanityUrl vanityUrl, final List<Map.Entry<String, VanityUrl>> toDelete) throws RepositoryException, NonUniqueUrlMappingException { NonUniqueUrlMappingException ex = JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(null, Constants.EDIT_WORKSPACE, null, new JCRCallback<NonUniqueUrlMappingException>() { public NonUniqueUrlMappingException doInJCR(JCRSessionWrapper session) throws RepositoryException { try { checkUniqueConstraint(contentNode, vanityUrl, toDelete, session); } catch (NonUniqueUrlMappingException e) { return e; } return null; } }); if (ex != null) { throw ex; } ex = JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(null, Constants.LIVE_WORKSPACE, null, new JCRCallback<NonUniqueUrlMappingException>() { public NonUniqueUrlMappingException doInJCR(JCRSessionWrapper session) throws RepositoryException { try { checkUniqueConstraint(contentNode, vanityUrl, toDelete, session); } catch (NonUniqueUrlMappingException e) { return e; } return null; } }); if (ex != null) { throw ex; } } private void checkUniqueConstraint(JCRNodeWrapper contentNode, VanityUrl vanityUrl, List<Map.Entry<String, VanityUrl>> toDelete, JCRSessionWrapper session) throws RepositoryException, NonUniqueUrlMappingException { List<VanityUrl> existingUrls = findExistingVanityUrls(vanityUrl.getUrl(), vanityUrl.getSite(), session); if (existingUrls != null && !existingUrls.isEmpty()) { for (VanityUrl existingUrl : existingUrls) { if (!vanityUrl.equals(existingUrl)) { boolean oldMatchWillBeDeleted = false; if (toDelete != null) { for (Map.Entry<String, VanityUrl> entry : toDelete) { if (existingUrl.equals(entry.getValue())) { oldMatchWillBeDeleted = true; break; } } } if (!oldMatchWillBeDeleted) { throw new NonUniqueUrlMappingException(vanityUrl.getUrl(), contentNode.getPath(), StringUtils.removeEnd( session.getNodeByUUID(existingUrl.getIdentifier()).getParent().getPath(), "/" + VANITYURLMAPPINGS_NODE), session.getWorkspace().getName()); } } } } } private VanityUrl populateJCRData(JCRNodeWrapper node, VanityUrl itemToBePopulated) throws RepositoryException { itemToBePopulated.setIdentifier(node.getIdentifier()); itemToBePopulated.setPath(node.getPath()); itemToBePopulated.setSite(JCRContentUtils.getSiteKey(node.getPath())); itemToBePopulated.setUrl(node.getPropertyAsString(PROPERTY_URL)); itemToBePopulated.setLanguage(node.getPropertyAsString(JCR_LANGUAGE)); itemToBePopulated.setActive(node.getProperty(PROPERTY_ACTIVE).getBoolean()); itemToBePopulated.setDefaultMapping(node.getProperty(PROPERTY_DEFAULT).getBoolean()); return itemToBePopulated; } }