001/* 002 * Copyright 2018 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.context.Context; 026import org.apache.avalon.framework.context.ContextException; 027import org.apache.avalon.framework.context.Contextualizable; 028import org.apache.cocoon.components.ContextHelper; 029import org.apache.cocoon.environment.Request; 030 031import org.ametys.cms.tag.AbstractTagProvider; 032import org.ametys.cms.tag.Tag; 033import org.ametys.plugins.repository.AmetysObject; 034import org.ametys.plugins.repository.AmetysObjectIterable; 035import org.ametys.plugins.repository.AmetysRepositoryException; 036import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 037import org.ametys.plugins.repository.TraversableAmetysObject; 038 039/** 040 * Abstract class representing a jcr tag provider. <br> 041 * @param <T> the tag class 042 */ 043public abstract class AbstractJCRTagProvider<T extends Tag> extends AbstractTagProvider<T> implements Contextualizable 044{ 045 /** The request attribute name with cache information */ 046 protected static final String CACHE_REQUEST_ATTRIBUTE = AbstractJCRTagProvider.class.getName() + "$cache"; 047 048 /** The context */ 049 protected Context _context; 050 051 @Override 052 public void configure(Configuration configuration) throws ConfigurationException 053 { 054 super.configure(configuration); 055 _id = configuration.getAttribute("id"); 056 } 057 058 @Override 059 public void contextualize(Context context) throws ContextException 060 { 061 _context = context; 062 } 063 064 /** 065 * Get the Map of tags by their unique name 066 * @param contextualParameters The contextual parameters 067 * @return The non null cache 068 * @throws RepositoryException If the cache cannot be filled 069 */ 070 protected Map<String, T> _getCache(Map<String, Object> contextualParameters) throws RepositoryException 071 { 072 Request request = ContextHelper.getRequest(_context); 073 if (request == null) 074 { 075 return new HashMap<>(); 076 } 077 078 @SuppressWarnings("unchecked") 079 Map<String, T> cache = (Map<String, T>) request.getAttribute(CACHE_REQUEST_ATTRIBUTE + "$" + getId()); 080 if (cache == null) 081 { 082 cache = new HashMap<>(); 083 request.setAttribute(CACHE_REQUEST_ATTRIBUTE + "$" + getId(), cache); 084 085 TraversableAmetysObject rootNode = getRootNode(contextualParameters); 086 _fillCache(rootNode, null, cache); 087 } 088 089 return cache; 090 } 091 092 /** 093 * Clear the request cache 094 */ 095 public void clearCache() 096 { 097 Request request = ContextHelper.getRequest(_context); 098 if (request != null) 099 { 100 request.removeAttribute(CACHE_REQUEST_ATTRIBUTE + "$" + getId()); 101 } 102 } 103 104 @Override 105 public Map<String, T> getTags(Map<String, Object> contextualParameters) 106 { 107 Map<String, T> tags = new HashMap<>(); 108 109 try 110 { 111 Map<String, T> cache = _getCache(contextualParameters); 112 113 TraversableAmetysObject rootNode = getRootNode(contextualParameters); 114 115 try (AmetysObjectIterable<AmetysObject> it = rootNode.getChildren()) 116 { 117 for (AmetysObject object : it) 118 { 119 if (object instanceof JCRTag) 120 { 121 T tag = cache.get(object.getName()); 122 tags.put(object.getId(), tag); 123 } 124 } 125 } 126 } 127 catch (RepositoryException e) 128 { 129 getLogger().error("Unable to get JCR tags", e); 130 } 131 132 return tags; 133 } 134 135 @Override 136 public T getTag(String tagName, Map<String, Object> contextualParameters) 137 { 138 try 139 { 140 Map<String, T> cache = _getCache(contextualParameters); 141 return cache.get(tagName); 142 } 143 catch (RepositoryException e) 144 { 145 getLogger().error("Unable to get JCR tags", e); 146 return null; 147 } 148 } 149 150 @Override 151 public boolean hasTag(String tagName, Map<String, Object> contextualParameters) 152 { 153 try 154 { 155 Map<String, T> cache = _getCache(contextualParameters); 156 return cache.containsKey(tagName); 157 } 158 catch (RepositoryException e) 159 { 160 getLogger().error("Unable to get JCR tags", e); 161 return false; 162 } 163 } 164 165 /** 166 * Fill cache 167 * @param parentTagNode The parent tag node 168 * @param parentTag The parent tag 169 * @param cache The cache 170 * @throws RepositoryException If an error occurred 171 */ 172 protected abstract void _fillCache(TraversableAmetysObject parentTagNode, T parentTag, Map<String, T> cache) throws RepositoryException; 173 174 175 /** 176 * Get the root node for tags 177 * @param contextualParameters The contextual parameters 178 * @return The root node 179 * @throws RepositoryException if an error occurred 180 */ 181 public abstract ModifiableTraversableAmetysObject getRootNode (Map<String, Object> contextualParameters) throws RepositoryException; 182 183 /** 184 * Get a node or create it if not exist. 185 * @param parentNode the parent node 186 * @param nodeName the node name 187 * @param nodeType the node type 188 * @return the node 189 * @throws AmetysRepositoryException if an error occurred 190 */ 191 protected ModifiableTraversableAmetysObject _getOrCreateNode(ModifiableTraversableAmetysObject parentNode, String nodeName, String nodeType) throws AmetysRepositoryException 192 { 193 ModifiableTraversableAmetysObject definitionsNode; 194 if (parentNode.hasChild(nodeName)) 195 { 196 definitionsNode = parentNode.getChild(nodeName); 197 } 198 else 199 { 200 definitionsNode = parentNode.createChild(nodeName, nodeType); 201 parentNode.saveChanges(); 202 } 203 return definitionsNode; 204 } 205 206}