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 */ 016 017package org.ametys.plugins.repository.provider; 018 019import java.io.File; 020import java.util.HashMap; 021import java.util.Map; 022 023import javax.jcr.LoginException; 024import javax.jcr.NoSuchWorkspaceException; 025import javax.jcr.RepositoryException; 026import javax.jcr.Session; 027import javax.jcr.SimpleCredentials; 028 029import org.apache.avalon.framework.CascadingRuntimeException; 030import org.apache.avalon.framework.activity.Disposable; 031import org.apache.avalon.framework.activity.Initializable; 032import org.apache.avalon.framework.component.Component; 033import org.apache.avalon.framework.context.ContextException; 034import org.apache.avalon.framework.context.Contextualizable; 035import org.apache.avalon.framework.service.ServiceException; 036import org.apache.avalon.framework.service.ServiceManager; 037import org.apache.cocoon.Constants; 038import org.apache.cocoon.components.ContextHelper; 039import org.apache.cocoon.environment.Context; 040import org.apache.cocoon.environment.Request; 041import org.apache.jackrabbit.core.cache.CacheManager; 042import org.apache.jackrabbit.core.config.ConfigurationException; 043import org.apache.jackrabbit.core.config.RepositoryConfig; 044 045import org.ametys.core.datasource.ConnectionHelper; 046import org.ametys.plugins.repository.RepositoryConstants; 047import org.ametys.runtime.config.Config; 048import org.ametys.runtime.util.AmetysHomeHelper; 049 050/** 051 * JackrabbitRepository is a JCR repository component based on Jackrabbit 052 */ 053public class JackrabbitRepository extends AbstractRepository implements LogoutManager, Initializable, Contextualizable, Disposable, Component 054{ 055 private org.apache.avalon.framework.context.Context _avalonContext; 056 private Context _context; 057 058 @Override 059 public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException 060 { 061 _avalonContext = context; 062 _context = (Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT); 063 } 064 065 @Override 066 public void service(ServiceManager smanager) throws ServiceException 067 { 068 super.service(smanager); 069 // Make sure the ConnectionHelper is initialized first, so the SQLDataSourceManager can be used in AmetysPersistenceManager 070 smanager.lookup(ConnectionHelper.ROLE); 071 } 072 073 074 public void initialize() throws Exception 075 { 076 RepositoryConfig repositoryConfig = createRepositoryConfig(); 077 AmetysRepository repo = new AmetysRepository(repositoryConfig); 078 079 _delegate = repo; 080 ((AmetysRepository) _delegate).setLogoutManager(this); 081 082 Long cacheSize = Config.getInstance().getValueAsLong("org.ametys.plugins.repository.cache"); 083 if (cacheSize == null || cacheSize <= 0) 084 { 085 cacheSize = 16777216L; // default value; 086 } 087 088 CacheManager cacheManager = repo.getCacheManager(); 089 cacheManager.setMaxMemory(cacheSize); 090 cacheManager.setMaxMemoryPerCache(cacheSize / 4); 091 cacheManager.setMinMemoryPerCache(cacheSize / 128); 092 093 // Register the delegate repository in the context for the repository workspace. 094 _context.setAttribute(CONTEXT_REPOSITORY_KEY, _delegate); 095 _context.setAttribute(CONTEXT_CREDENTIALS_KEY, new SimpleCredentials("ametys", new char[]{})); 096 _context.setAttribute(CONTEXT_IS_JNDI_KEY, false); 097 098 if (getLogger().isDebugEnabled()) 099 { 100 getLogger().debug("JCR Repository running"); 101 } 102 } 103 104 public void dispose() 105 { 106 AmetysRepository repo = (AmetysRepository) _delegate; 107 repo.setLogoutManager(null); 108 repo.shutdown(); 109 } 110 111 /** 112 * Create the repository configuration from the configuration file 113 * @return The repository configuration 114 * @throws ConfigurationException if an error occurred 115 */ 116 RepositoryConfig createRepositoryConfig() throws ConfigurationException 117 { 118 String config = _context.getRealPath("/WEB-INF/param/repository.xml"); 119 120 File homeFile = new File(AmetysHomeHelper.getAmetysHomeData(), "repository"); 121 122 if (getLogger().isDebugEnabled()) 123 { 124 getLogger().debug("Creating JCR Repository config at: " + homeFile.getAbsolutePath()); 125 } 126 127 return RepositoryConfig.create(config, homeFile.getAbsolutePath()); 128 } 129 130 int getSessionCount() 131 { 132 return ((AmetysRepository) _delegate).getSessionCount(); 133 } 134 135 public Session login(String workspace) throws LoginException, NoSuchWorkspaceException, RepositoryException 136 { 137 try 138 { 139 if (getLogger().isDebugEnabled()) 140 { 141 getLogger().debug("Getting JCR Session"); 142 } 143 144 try 145 { 146 Request request = ContextHelper.getRequest(_avalonContext); 147 148 @SuppressWarnings("unchecked") 149 Map<String, Session> sessions = (Map<String, Session>) request.getAttribute(RepositoryConstants.JCR_SESSION_REQUEST_ATTRIBUTE); 150 151 if (sessions == null) 152 { 153 sessions = new HashMap<>(); 154 request.setAttribute(RepositoryConstants.JCR_SESSION_REQUEST_ATTRIBUTE, sessions); 155 } 156 157 Session session = sessions.get(workspace); 158 159 if (session == null || !session.isLive()) 160 { 161 session = _delegate.login(new SimpleCredentials("ametys", new char[]{}), workspace); 162 sessions.put(workspace, session); 163 } 164 165 return session; 166 } 167 catch (CascadingRuntimeException e) 168 { 169 if (e.getCause() instanceof ContextException) 170 { 171 // Unable to get request. Must be in another thread or at init time. 172 Session session = _delegate.login(new SimpleCredentials("ametys", new char[]{}), workspace); 173 return session; 174 } 175 else 176 { 177 throw e; 178 } 179 } 180 } 181 catch (RepositoryException e) 182 { 183 throw e; 184 } 185 catch (Exception e) 186 { 187 throw new RuntimeException("Unable to get Session", e); 188 } 189 } 190 191 public void logout(Session session) 192 { 193 if (!(session instanceof AmetysSession)) 194 { 195 throw new IllegalArgumentException("JCR Session should be an instance of AmetysSession"); 196 } 197 198 AmetysSession ametysSession = (AmetysSession) session; 199 200 if (getLogger().isDebugEnabled()) 201 { 202 getLogger().debug("Logging out AmetysSession"); 203 } 204 205 try 206 { 207 // the following statement will fail if we are not processing a request. 208 ContextHelper.getRequest(_avalonContext); 209 210 // does nothing as the session will be actually logged out at the end of the request. 211 if (getLogger().isDebugEnabled()) 212 { 213 getLogger().debug("AmetysSession logout delayed until the end of the HTTP request."); 214 } 215 216 } 217 catch (Exception e) 218 { 219 // unable to get request. Must be in another thread or at init time. 220 if (getLogger().isDebugEnabled()) 221 { 222 getLogger().debug("Not in a request. AmetysSession will be actually logged out."); 223 } 224 225 ametysSession.forceLogout(); 226 } 227 } 228}