| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.jcr; |
| |
| import org.apache.avalon.framework.activity.Disposable; |
| import org.apache.avalon.framework.component.Component; |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.configuration.ConfigurationException; |
| import org.apache.avalon.framework.context.Context; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.context.Contextualizable; |
| import org.apache.avalon.framework.logger.AbstractLogEnabled; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.avalon.framework.service.Serviceable; |
| import org.apache.avalon.framework.thread.ThreadSafe; |
| |
| import org.apache.cocoon.components.ContextHelper; |
| import org.apache.cocoon.components.treeprocessor.variables.VariableResolver; |
| import org.apache.cocoon.components.treeprocessor.variables.VariableResolverFactory; |
| import org.apache.cocoon.environment.ObjectModelHelper; |
| import org.apache.cocoon.environment.Request; |
| import org.apache.cocoon.sitemap.PatternException; |
| |
| import org.apache.excalibur.source.SourceResolver; |
| import org.apache.excalibur.source.impl.FileSource; |
| |
| import javax.jcr.Credentials; |
| import javax.jcr.LoginException; |
| import javax.jcr.NoSuchWorkspaceException; |
| import javax.jcr.Repository; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Session; |
| import javax.jcr.SimpleCredentials; |
| import java.util.Map; |
| |
| /** |
| * Base class for JCR (aka <a |
| * href="http://www.jcp.org/en/jsr/detail?id=170">JSR-170</a>) repository as |
| * a Cocoon component. The main purpose of this class is to allow repository |
| * credentials to be specified in the component's configuration, so that the |
| * application code just has to call <code>repository.login()</code>. |
| * |
| * <p> |
| * There is no Cocoon-specific role for this component: "<code>javax.jcr.Repository</code>" |
| * should be used. |
| * |
| * <p> |
| * The configuration of this class, inherited by its subclasses, is as follows: |
| * |
| * <pre> |
| * <jcr-repository> |
| * <jaas src="context://samples/jaas.config"/> |
| * <credentials login="<i>expression</i>" password="<i>expression</i>"/> |
| * ... other specific configuration... |
| * </jcr-repository> |
| * </pre> |
| * |
| * Login and password can be specified using the sitemap expression language, |
| * thus allowing the use of input modules to compute their values, e.g. |
| * <code>password="{session-attr:jcr-password}"</code>. |
| * |
| * <p> |
| * <code><credentials></code> is optional. If not specified, the |
| * application must explicitely supply credentials when calling |
| * <code>Repository.login()</code>. |
| * |
| * @version $Id$ |
| */ |
| public abstract class AbstractRepository extends AbstractLogEnabled |
| implements Repository, ThreadSafe, Contextualizable, Serviceable, |
| Configurable, Disposable, Component { |
| |
| /** |
| * Role which shall be used for JCR repository implementations. |
| */ |
| public static final String ROLE = "javax.jcr.Repository"; |
| |
| /** |
| * The request attribute in which the JCR session is stored |
| */ |
| public static final String JCR_SESSION_REQUEST_ATTRIBUTE = "jcr-session"; |
| |
| protected ServiceManager manager; |
| |
| protected Context context; |
| |
| protected Repository delegate; |
| |
| // Defined by the portal block :-( |
| // protected VariableResolverFactory variableFactory; |
| |
| protected VariableResolver loginResolver; |
| |
| protected VariableResolver passwordResolver; |
| |
| |
| // ============================================================================================= |
| // Avalon lifecycle |
| // ============================================================================================= |
| |
| public void contextualize(Context context) throws ContextException { |
| this.context = context; |
| } |
| |
| public void service(ServiceManager manager) throws ServiceException { |
| this.manager = manager; |
| } |
| |
| public void configure(Configuration config) throws ConfigurationException { |
| // FIXME FIXME FIXME Hack setting system jaas property |
| Configuration jaas = config.getChild("jaas", false); |
| if (jaas != null) { |
| String jaasURI = jaas.getAttribute("src"); |
| SourceResolver resolver = null; |
| FileSource jaasSrc = null; |
| try { |
| resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE); |
| jaasSrc = (FileSource) resolver.resolveURI(jaasURI); |
| if (System.getProperty("java.security.auth.login.config") == null) { |
| System.setProperty("java.security.auth.login.config", jaasSrc.getFile().getAbsolutePath()); |
| } else { |
| // WARNING: java.security.auth.login.config has already been set |
| } |
| } catch (Exception e) { |
| throw new ConfigurationException("Cannot resolve jaas URI: " + jaasURI + " at " + config.getLocation()); |
| } finally { |
| if (jaasSrc != null) { |
| resolver.release(jaasSrc); |
| } |
| this.manager.release(resolver); |
| } |
| } |
| |
| Configuration credentials = config.getChild("credentials", false); |
| if (credentials != null) { |
| String login = credentials.getAttribute("login"); |
| String password = credentials.getAttribute("password"); |
| |
| try { |
| this.loginResolver = VariableResolverFactory.getResolver(login, this.manager); |
| } catch (PatternException e) { |
| throw new ConfigurationException("Invalid expression for 'login' at " + credentials.getLocation(), e); |
| } |
| try { |
| this.passwordResolver = VariableResolverFactory.getResolver(password, this.manager); |
| } catch (PatternException e) { |
| if (this.loginResolver instanceof Disposable) { |
| ((Disposable) this.loginResolver).dispose(); |
| } |
| this.loginResolver = null; |
| throw new ConfigurationException("Invalid expression for 'password' at " + credentials.getLocation(), e); |
| } |
| } |
| } |
| |
| public void dispose() { |
| this.context = null; |
| this.delegate = null; |
| |
| if (this.loginResolver instanceof Disposable) { |
| ((Disposable) this.loginResolver).dispose(); |
| } |
| this.loginResolver = null; |
| if (this.passwordResolver instanceof Disposable) { |
| ((Disposable) this.passwordResolver).dispose(); |
| } |
| this.passwordResolver = null; |
| this.manager = null; |
| } |
| |
| // ============================================================================================= |
| // Repository interface |
| // ============================================================================================= |
| |
| public String getDescriptor(String key) { |
| return delegate.getDescriptor(key); |
| } |
| |
| public String[] getDescriptorKeys() { |
| return delegate.getDescriptorKeys(); |
| } |
| |
| public Session login() |
| throws LoginException, NoSuchWorkspaceException, RepositoryException { |
| Session session = getCachedSession(null); |
| |
| if (session == null) { |
| Credentials creds = getCredentials(); |
| session = creds == null ? delegate.login() : delegate.login(creds); |
| cacheSession(session, null); |
| } |
| |
| return session; |
| } |
| |
| public Session login(Credentials creds) |
| throws LoginException, NoSuchWorkspaceException, RepositoryException { |
| Session session = getCachedSession(null); |
| |
| if (session == null) { |
| session = delegate.login(creds); |
| cacheSession(session, null); |
| } |
| |
| return session; |
| } |
| |
| public Session login(Credentials creds, String workspace) |
| throws LoginException, NoSuchWorkspaceException, RepositoryException { |
| Session session = getCachedSession(workspace); |
| |
| if (session == null) { |
| session = delegate.login(creds, workspace); |
| cacheSession(session, workspace); |
| } |
| |
| return session; |
| } |
| |
| public Session login(String workspace) |
| throws LoginException, NoSuchWorkspaceException, RepositoryException { |
| Session session = getCachedSession(workspace); |
| |
| if (session == null) { |
| Credentials creds = getCredentials(); |
| session = creds == null ? delegate.login(workspace) : delegate.login(creds, workspace); |
| cacheSession(session, workspace); |
| } |
| |
| return session; |
| } |
| |
| // TODO: When logout should be called? |
| |
| // ============================================================================================= |
| // Implementation methods |
| // ============================================================================================= |
| |
| private Session getCachedSession(String workspace) { |
| Map objectModel; |
| try { |
| objectModel = ContextHelper.getObjectModel(context); |
| } catch(Exception e) { |
| // We don't have an object model (happens e.g. at init time or in a cron job) |
| return null; |
| } |
| |
| String attributeName = workspace == null ? |
| JCR_SESSION_REQUEST_ATTRIBUTE : JCR_SESSION_REQUEST_ATTRIBUTE + "/" + workspace; |
| |
| Request request = ObjectModelHelper.getRequest(objectModel); |
| //FIXME: request is null when running in a testcase |
| if (request == null) return null; |
| Session session = (Session) request.getAttribute(attributeName); |
| |
| return (session != null && session.isLive()) ? session : null; |
| } |
| |
| private void cacheSession(Session session, String workspace) { |
| Map objectModel; |
| try { |
| objectModel = ContextHelper.getObjectModel(context); |
| } catch(Exception e) { |
| // We don't have an object model (happens e.g. at init time or in a cron job) |
| return; |
| } |
| |
| String attributeName = workspace == null ? |
| JCR_SESSION_REQUEST_ATTRIBUTE : JCR_SESSION_REQUEST_ATTRIBUTE + "/" + workspace; |
| |
| Request request = ObjectModelHelper.getRequest(objectModel); |
| //FIXME: request is null when running in a testcase |
| if (request == null) return; |
| request.setAttribute(attributeName, session); |
| } |
| |
| private Credentials getCredentials() throws LoginException { |
| if (this.loginResolver != null) { |
| |
| Map objectModel; |
| try { |
| objectModel = ContextHelper.getObjectModel(context); |
| } catch(Exception e) { |
| // We don't have an object model (happens e.g. at init time or in a cron job) |
| throw new LoginException("No objectModel to evaluate credentials", e); |
| } |
| |
| try { |
| String login = this.loginResolver.resolve(objectModel); |
| String password = this.loginResolver.resolve(objectModel); |
| return new SimpleCredentials(login, password.toCharArray()); |
| } catch (PatternException e) { |
| throw new LoginException("Failed to evaluate credentials", e); |
| } |
| } else { |
| return null; |
| } |
| } |
| } |