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