001/* 002 * Copyright 2010 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.plugins.repository.jcr; 017 018import java.util.Collection; 019import java.util.Collections; 020 021import javax.jcr.ItemNotFoundException; 022import javax.jcr.Node; 023import javax.jcr.Repository; 024import javax.jcr.RepositoryException; 025import javax.jcr.Session; 026 027import org.apache.avalon.framework.configuration.Configurable; 028import org.apache.avalon.framework.configuration.Configuration; 029import org.apache.avalon.framework.configuration.ConfigurationException; 030import org.apache.avalon.framework.logger.AbstractLogEnabled; 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.avalon.framework.service.Serviceable; 034 035import org.ametys.plugins.repository.AmetysObject; 036import org.ametys.plugins.repository.AmetysObjectFactoryExtensionPoint; 037import org.ametys.plugins.repository.AmetysObjectResolver; 038import org.ametys.plugins.repository.AmetysRepositoryException; 039import org.ametys.plugins.repository.UnknownAmetysObjectException; 040import org.ametys.plugins.repository.provider.AbstractRepository; 041 042/** 043 * Default implementation of an {@link JCRAmetysObjectFactory}, 044 * handling {@link SimpleAmetysObject}.<br> 045 * This implementation takes its scheme and nodetype through a configuration:<br> 046 * <code> 047 * <extension point="org.ametys.plugins.repository.AmetysObjectFactoryExtensionPoint"<br> 048 * id="XXXX" 049 * class="org.ametys.plugins.repository.DefaultAmetysObjectFactory"><br> 050 * <scheme>your_scheme</scheme><br> 051 * <nodetype>your:nodetype</nodetype><br> 052 * [<nodetype>your:nodetype2</nodetype>]<br> 053 * [...]<br> 054 * </extension><br> 055 * This implementation manages only one nodetype. 056 * </code> 057 */ 058public class SimpleAmetysObjectFactory extends AbstractLogEnabled implements JCRAmetysObjectFactory<SimpleAmetysObject>, Configurable, Serviceable 059{ 060 /** The application {@link AmetysObjectResolver} */ 061 protected AmetysObjectResolver _resolver; 062 063 /** The {@link AmetysObjectFactoryExtensionPoint} */ 064 protected AmetysObjectFactoryExtensionPoint _ametysFactoryExtensionPoint; 065 066 /** The configured scheme */ 067 protected String _scheme; 068 069 /** The configured nodetype */ 070 protected String _nodetype; 071 072 /** JCR Repository */ 073 protected Repository _repository; 074 075 /** The Avalon {@link ServiceManager} */ 076 protected ServiceManager _manager; 077 078 public void service(ServiceManager manager) throws ServiceException 079 { 080 _manager = manager; 081 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 082 _repository = (Repository) manager.lookup(AbstractRepository.ROLE); 083 _ametysFactoryExtensionPoint = (AmetysObjectFactoryExtensionPoint) manager.lookup(AmetysObjectFactoryExtensionPoint.ROLE); 084 } 085 086 public void configure(Configuration configuration) throws ConfigurationException 087 { 088 _scheme = configuration.getChild("scheme").getValue(); 089 090 Configuration[] nodetypesConf = configuration.getChildren("nodetype"); 091 092 if (nodetypesConf.length != 1) 093 { 094 throw new ConfigurationException("A SimpleAmetysObjectFactory must have one and only one associated nodetype. " 095 + "The '" + configuration.getAttribute("id") + "' component has " + nodetypesConf.length); 096 } 097 098 _nodetype = nodetypesConf[0].getValue(); 099 } 100 101 AmetysObjectResolver _getResolver() 102 { 103 return _resolver; 104 } 105 106 public String getScheme() 107 { 108 return _scheme; 109 } 110 111 public Collection<String> getNodetypes() 112 { 113 return Collections.singletonList(_nodetype); 114 } 115 116 @SuppressWarnings("unchecked") 117 public SimpleAmetysObject getAmetysObject(Node node, String parentPath) throws AmetysRepositoryException, RepositoryException 118 { 119 return new SimpleAmetysObject(node, parentPath, this); 120 } 121 122 public SimpleAmetysObject getAmetysObjectById(String id) throws AmetysRepositoryException 123 { 124 try 125 { 126 return getAmetysObjectById(id, null); 127 } 128 catch (RepositoryException e) 129 { 130 throw new AmetysRepositoryException("Unable to get AmetysObject for id: " + id, e); 131 } 132 } 133 134 @Override 135 public SimpleAmetysObject getAmetysObjectById(String id, Session session) throws AmetysRepositoryException, RepositoryException 136 { 137 Node node = getNode(id, session); 138 139 if (!node.getPath().startsWith('/' + AmetysObjectResolver.ROOT_REPO)) 140 { 141 throw new AmetysRepositoryException("Cannot resolve a Node outside Ametys tree"); 142 } 143 144 return getAmetysObject(node, null); 145 } 146 147 public boolean hasAmetysObjectForId(String id) throws AmetysRepositoryException 148 { 149 try 150 { 151 getNode(id, null); 152 return true; 153 } 154 catch (UnknownAmetysObjectException e) 155 { 156 return false; 157 } 158 } 159 160 /** 161 * Returns the JCR Node associated with the given object id.<br> 162 * This implementation assumes that the id is like <code><scheme>://<uuid></code> 163 * @param id the unique id of the object 164 * @param session the JCR Session to use to retrieve the Node. 165 * @return the JCR Node associated with the given id 166 */ 167 protected Node getNode(String id, Session session) 168 { 169 // l'id est de la forme <scheme>://uuid 170 String uuid = id.substring(getScheme().length() + 3); 171 172 Session jcrSession = null; 173 try 174 { 175 jcrSession = session != null ? session : _repository.login(); 176 Node node = jcrSession.getNodeByIdentifier(uuid); 177 return node; 178 } 179 catch (ItemNotFoundException e) 180 { 181 if (session == null && jcrSession != null) 182 { 183 // logout only if the session was created here 184 jcrSession.logout(); 185 } 186 187 throw new UnknownAmetysObjectException("There's no node for id " + id, e); 188 } 189 catch (RepositoryException e) 190 { 191 if (session == null && jcrSession != null) 192 { 193 // logout only if the session was created here 194 jcrSession.logout(); 195 } 196 197 throw new AmetysRepositoryException("Unable to get AmetysObject for id: " + id, e); 198 } 199 } 200 201 /** 202 * Returns the parent of the given {@link SimpleAmetysObject} . 203 * @param object a {@link SimpleAmetysObject}. 204 * @return the parent of the given {@link SimpleAmetysObject}. 205 * @throws AmetysRepositoryException if an error occurs. 206 */ 207 public AmetysObject getParent(SimpleAmetysObject object) throws AmetysRepositoryException 208 { 209 if (getLogger().isDebugEnabled()) 210 { 211 getLogger().debug("Entering DefaultTraversableAmetysObjectFactory.getParent with object of name: " + object.getName()); 212 } 213 214 Node node = getWorkspaceNode (object); 215 216 try 217 { 218 Node parentNode = node.getParent(); 219 String parentNodetype = NodeTypeHelper.getNodeTypeName(parentNode); 220 221 if (getLogger().isDebugEnabled()) 222 { 223 getLogger().debug("Parent nodetype is " + parentNodetype); 224 } 225 226 if (parentNodetype.equals(_nodetype)) 227 { 228 if (getLogger().isDebugEnabled()) 229 { 230 getLogger().debug("The parent node has the same nodetype than this ObjectFactory: " + _nodetype); 231 } 232 233 // si le nodetype est celui de la factory, on peut éviter de passer par le resolver 234 return getAmetysObject(parentNode, null); 235 } 236 237 return _resolver.resolve(parentNode, false); 238 } 239 catch (RepositoryException e) 240 { 241 throw new AmetysRepositoryException("Unable to retrieve parent object of object " + object.getName(), e); 242 } 243 } 244 245 /** 246 * Returns the JCR node backing this {@link SimpleAmetysObject} in the JCR workspace. May be overridden to deal with e.g. versionning 247 * @param object a {@link SimpleAmetysObject}. 248 * @return the JCR node backing this {@link SimpleAmetysObject}. 249 */ 250 protected Node getWorkspaceNode(SimpleAmetysObject object) 251 { 252 return object.getNode(); 253 } 254}