001/* 002 * Copyright 2014 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.tag.jcr; 017 018import java.util.HashMap; 019import java.util.Map; 020 021import javax.jcr.RepositoryException; 022 023import org.apache.avalon.framework.configuration.Configuration; 024import org.apache.avalon.framework.configuration.ConfigurationException; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.cocoon.components.ContextHelper; 028import org.apache.cocoon.environment.Request; 029 030import org.ametys.cms.tag.StaticTagProvider; 031import org.ametys.cms.tag.Tag; 032import org.ametys.cms.tag.TagTargetType; 033import org.ametys.cms.tag.TagTargetTypeExtensionPoint; 034import org.ametys.plugins.repository.AmetysObject; 035import org.ametys.plugins.repository.AmetysObjectIterable; 036import org.ametys.plugins.repository.AmetysObjectResolver; 037import org.ametys.plugins.repository.AmetysRepositoryException; 038import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 039import org.ametys.plugins.repository.RepositoryConstants; 040import org.ametys.plugins.repository.TraversableAmetysObject; 041import org.ametys.runtime.i18n.I18nizableText; 042 043/** 044 * Class representing a jcr tag provider. <br> 045 */ 046public class JCRTagProvider extends StaticTagProvider 047{ 048 /** Constant for plugin node name */ 049 public static final String PLUGIN_NODE_NAME = "cms"; 050 /** JCR nodetype for tags */ 051 public static final String TAGS_NODETYPE = RepositoryConstants.NAMESPACE_PREFIX + ":tags"; 052 /** JCR node name for tags */ 053 public static final String TAGS_NODENAME = "tags"; 054 055 /** The request attribute name with cache information */ 056 protected static final String CACHE_REQUEST_ATTRIBUTE = JCRTagProvider.class.getName() + "$cache"; 057 058 /** The Ametys object resolver */ 059 protected AmetysObjectResolver _resolver; 060 061 @Override 062 public void configure(Configuration configuration) throws ConfigurationException 063 { 064 _id = configuration.getAttribute("id"); 065 _label = configureLabel(configuration, "plugin." + _pluginName); 066 _description = configureDescription(configuration, "plugin." + _pluginName); 067 } 068 069 @Override 070 public void service(ServiceManager smanager) throws ServiceException 071 { 072 super.service(smanager); 073 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 074 _targetTypeEP = (TagTargetTypeExtensionPoint) smanager.lookup(TagTargetTypeExtensionPoint.ROLE); 075 } 076 077 078 /** 079 * Get the Map of tags by their unique name 080 * @param contextualParameters The contextual parameters 081 * @return The non null cache 082 * @throws RepositoryException If the cache cannot be filled 083 */ 084 protected Map<String, Object> _getCache(Map<String, Object> contextualParameters) throws RepositoryException 085 { 086 Request request = ContextHelper.getRequest(_context); 087 if (request == null) 088 { 089 return new HashMap<>(); 090 } 091 092 @SuppressWarnings("unchecked") 093 Map<String, Object> cache = (Map<String, Object>) request.getAttribute(CACHE_REQUEST_ATTRIBUTE); 094 if (cache == null) 095 { 096 cache = new HashMap<>(); 097 request.setAttribute(CACHE_REQUEST_ATTRIBUTE, cache); 098 099 TraversableAmetysObject rootNode = getRootNode(contextualParameters); 100 _fillCache(rootNode, null, cache); 101 } 102 103 return cache; 104 } 105 106 /** 107 * Fill cache 108 * @param parentTagNode The parent tag node 109 * @param parentTag The parent tag 110 * @param cache The cache 111 * @throws RepositoryException If an error occurred 112 */ 113 protected void _fillCache(TraversableAmetysObject parentTagNode, Tag parentTag, Map<String, Object> cache) throws RepositoryException 114 { 115 for (AmetysObject child : parentTagNode.getChildren()) 116 { 117 if (child instanceof JCRTag) 118 { 119 JCRTag jcrTag = (JCRTag) child; 120 TagTargetType targeType = _targetTypeEP.getTagTargetType(jcrTag.getTargetType()); 121 Tag tag = new Tag(jcrTag.getId(), jcrTag.getName(), parentTag, new I18nizableText(jcrTag.getTitle()), new I18nizableText(jcrTag.getDescription()), jcrTag.getVisibility(), targeType); 122 cache.put(child.getName(), tag); 123 124 if (parentTag != null) 125 { 126 parentTag.addTag(tag); 127 } 128 129 _fillCache(jcrTag, tag, cache); 130 } 131 } 132 } 133 134 @Override 135 public Map<String, Tag> getTags(Map<String, Object> contextualParameters) 136 { 137 Map<String, Tag> tags = new HashMap<>(); 138 139 try 140 { 141 Map<String, Object> cache = _getCache(contextualParameters); 142 143 TraversableAmetysObject rootNode = getRootNode(contextualParameters); 144 145 try (AmetysObjectIterable<AmetysObject> it = rootNode.getChildren()) 146 { 147 for (AmetysObject object : it) 148 { 149 if (object instanceof JCRTag) 150 { 151 tags.put(object.getId(), (Tag) cache.get(object.getName())); 152 } 153 } 154 } 155 } 156 catch (RepositoryException e) 157 { 158 getLogger().error("Unable to get JCR tags", e); 159 } 160 161 return tags; 162 } 163 164 @Override 165 public boolean hasTag(String tagName, Map<String, Object> contextualParameters) 166 { 167 try 168 { 169 Map<String, Object> cache = _getCache(contextualParameters); 170 if (cache.containsKey(tagName)) 171 { 172 return cache.get(tagName) instanceof Tag; 173 } 174 else 175 { 176 return false; 177 } 178 } 179 catch (RepositoryException e) 180 { 181 getLogger().error("Unable to get JCR tags", e); 182 return false; 183 } 184 } 185 186 @Override 187 public Tag getTag(String tagName, Map<String, Object> contextualParameters) 188 { 189 try 190 { 191 Map<String, Object> cache = _getCache(contextualParameters); 192 Object o = cache.get(tagName); 193 if (o instanceof Tag) 194 { 195 return (Tag) o; 196 } 197 else 198 { 199 return null; 200 } 201 } 202 catch (RepositoryException e) 203 { 204 getLogger().error("Unable to get JCR tags", e); 205 return null; 206 } 207 } 208 209 /** 210 * Get the root node for tags 211 * @param contextualParameters The contextual parameters 212 * @return The root node 213 * @throws RepositoryException if an error occurred 214 */ 215 public ModifiableTraversableAmetysObject getRootNode (Map<String, Object> contextualParameters) throws RepositoryException 216 { 217 try 218 { 219 ModifiableTraversableAmetysObject pluginsNode = _resolver.resolveByPath("/ametys:plugins"); 220 221 ModifiableTraversableAmetysObject pluginNode = _getOrCreateNode(pluginsNode, PLUGIN_NODE_NAME, "ametys:unstructured"); 222 223 return _getOrCreateNode(pluginNode, TAGS_NODENAME, TAGS_NODETYPE); 224 } 225 catch (AmetysRepositoryException e) 226 { 227 throw new AmetysRepositoryException("Unable to get the JCR tags root node", e); 228 } 229 } 230 231 232 /** 233 * Get a node or create it if not exist. 234 * @param parentNode the parent node 235 * @param nodeName the node name 236 * @param nodeType the node type 237 * @return the node 238 * @throws AmetysRepositoryException if an error occurred 239 */ 240 protected ModifiableTraversableAmetysObject _getOrCreateNode(ModifiableTraversableAmetysObject parentNode, String nodeName, String nodeType) throws AmetysRepositoryException 241 { 242 ModifiableTraversableAmetysObject definitionsNode; 243 if (parentNode.hasChild(nodeName)) 244 { 245 definitionsNode = parentNode.getChild(nodeName); 246 } 247 else 248 { 249 definitionsNode = parentNode.createChild(nodeName, nodeType); 250 parentNode.saveChanges(); 251 } 252 return definitionsNode; 253 } 254 255 /** 256 * Creates the XPath query corresponding to specified tag name 257 * @param tagName the tag name 258 * @return the created XPath query 259 */ 260 public static String getXPathQuery(String tagName) 261 { 262 return "//element(" + tagName + "," + TagFactory.TAG_NODETYPE + ")"; 263 } 264 265}