/**
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.commons.chromattic;
import org.chromattic.api.Chromattic;
import org.chromattic.api.ChromatticBuilder;
import org.exoplatform.container.component.BaseComponentPlugin;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.PropertiesParam;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import java.util.*;
/**
* <p>The chromattic life cycle objets is a plugin that allow to bootstrap a chromattic builder and make
* it managed either locally or globally.</p>
*
* <p>It is allowed to create subclasses of this class to override the methods {@link #onOpenSession(SessionContext)}
* or {@link #onCloseSession(SessionContext)} to perform additional treatment on the session context at a precise
* phase of its life cycle.</p>
*
* <p>The life cycle domain uniquely identifies the chromattic domain among all domain registered against the
* {@link org.exoplatform.commons.chromattic.ChromatticManager} manager.</p>
*
* <p>The plugin takes an instance of {@link org.exoplatform.container.xml.InitParams} as parameter that contains
* the following entries:
*
* <ul>
* <li>The <code>domain-name</code> string that is the life cycle domain name</li>
* <li>The <code>workspace-name</code> string that is the repository workspace name associated with this life cycle</li>
* <li>The <code>entities</code> list value that contains the list of chromattic entities that will be registered
* against the builder chromattic builder</li>
* </ul>
* </p>
*
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
* @version $Revision$
*/
public class ChromatticLifeCycle extends BaseComponentPlugin
{
/** . */
private static final Map<String, ChromatticBuilder.Option<?>> RECOGNIZED_OPTIONS;
static
{
Map<String, ChromatticBuilder.Option<?>> options = new HashMap<String, ChromatticBuilder.Option<?>>();
for (ChromatticBuilder.Option<?> option : new ChromatticBuilder.Option<?>[]{ChromatticBuilder.JCR_OPTIMIZE_ENABLED,
ChromatticBuilder.JCR_OPTIMIZE_HAS_NODE_ENABLED,
ChromatticBuilder.JCR_OPTIMIZE_HAS_PROPERTY_ENABLED,
ChromatticBuilder.OBJECT_FORMATTER_CLASSNAME,
ChromatticBuilder.ROOT_NODE_PATH,
ChromatticBuilder.CREATE_ROOT_NODE})
{
options.put(option.getName(), option);
}
RECOGNIZED_OPTIONS = options;
}
/** . */
private final String domainName;
/** . */
private final String workspaceName;
/** . */
Chromattic realChromattic;
/** . */
private ChromatticImpl chromattic;
/** . */
ChromatticManager manager;
/** . */
private final List<String> entityClassNames;
/** . */
final ThreadLocal<LocalContext> currentContext = new ThreadLocal<LocalContext>();
/** . */
final Logger log = LoggerFactory.getLogger(ChromatticLifeCycle.class);
/** . */
private final Map<String, String> optionMap;
public ChromatticLifeCycle(InitParams params)
{
Map<String, String> options = new HashMap<String, String>();
PropertiesParam pp = params.getPropertiesParam("options");
if (pp != null)
{
options.putAll(pp.getProperties());
}
this.domainName = params.getValueParam("domain-name").getValue();
this.workspaceName = params.getValueParam("workspace-name").getValue();
this.entityClassNames = params.getValuesParam("entities").getValues();
this.optionMap = options;
}
public String getDomainName()
{
return domainName;
}
public final String getWorkspaceName()
{
return workspaceName;
}
public final Chromattic getChromattic()
{
return chromattic;
}
public final ChromatticManager getManager()
{
return manager;
}
AbstractContext getLoginContext()
{
Synchronization sync = manager.getSynchronization();
//
if (sync != null)
{
return sync.getContext(domainName);
}
//
return currentContext.get();
}
/**
* Returns <code>#getContext(false)</code>.
*
* @see #getContext(boolean)
* @return a session context
*/
public final SessionContext getContext()
{
return getContext(false);
}
/**
* A best effort to return a session context whether it's local or global.
*
* @param peek true if no context should be automatically created
* @return a session context
*/
public final SessionContext getContext(boolean peek)
{
log.trace("Requesting context");
Synchronization sync = manager.getSynchronization();
//
if (sync != null)
{
log.trace("Found synchronization about to get the current context for chromattic " + domainName);
SynchronizedContext context = sync.getContext(domainName);
//
if (context == null && !peek)
{
log.trace("No current context found, about to open one");
context = sync.openContext(this);
}
else
{
log.trace("Found a context and will return it");
}
//
return context;
}
//
log.trace("No active synchronization about to try the current local context");
LocalContext localContext = currentContext.get();
log.trace("Found local context " + localContext);
//
return localContext;
}
final SessionContext openSynchronizedContext()
{
log.trace("Opening a global context");
AbstractContext context = (AbstractContext)getContext(true);
//
if (context != null)
{
String msg = "A global context is already opened";
log.trace(msg);
throw new IllegalStateException(msg);
}
// Attempt to get the synchronization
log.trace("Ok, no global context found, asking current synchronization");
Synchronization sync = manager.getSynchronization();
//
if (sync == null)
{
String msg = "Need global synchronization for opening a global context";
log.trace(msg);
throw new IllegalStateException(msg);
}
//
log.trace("Opening a global context for the related sync");
return sync.openContext(this);
}
/**
* Opens a context and returns it. If there is a global ongoing synchronization then the context will be
* scoped to that synchronization, otherwise it will be a local context.
*
* @return the session context
*/
public final SessionContext openContext()
{
log.trace("Opening a context");
AbstractContext context = (AbstractContext)getContext(true);
//
if (context != null)
{
String msg = "A context is already opened";
log.trace(msg);
throw new IllegalStateException(msg);
}
//
log.trace("Ok, no context found, asking current synchronization");
Synchronization sync = manager.getSynchronization();
//
if (sync != null)
{
log.trace("Found a synchronization, about to open a global context");
context = sync.openContext(this);
}
else
{
log.trace("Not synchronization found, about to a local context");
LocalContext localContext = new LocalContext(this);
currentContext.set(localContext);
onOpenSession(localContext);
context = localContext;
}
//
return context;
}
public final void closeContext(boolean save)
{
log.trace("Requesting for context close with save=" + save + " going to look for any context");
AbstractContext context = (AbstractContext)getContext(true);
//
if (context == null)
{
String msg = "Cannot close non existing context";
log.trace(msg);
throw new IllegalStateException(msg);
}
//
context.close(save);
}
protected void onOpenSession(SessionContext context)
{
}
protected void onCloseSession(SessionContext context)
{
}
Session doLogin() throws RepositoryException
{
AbstractContext loginContext = getLoginContext();
//
if (loginContext == null)
{
throw new IllegalStateException("Could not obtain a login context");
}
//
return loginContext.doLogin();
}
public final void start() throws Exception
{
log.debug("About to setup Chromattic life cycle " + domainName);
ChromatticBuilder builder = ChromatticBuilder.create();
//
ClassLoader cl = Thread.currentThread().getContextClassLoader();
for (String className : entityClassNames)
{
String fqn = className.trim();
log.debug("Adding class " + fqn + " to life cycle " + domainName);
Class<?> entityClass = cl.loadClass(fqn);
builder.add(entityClass);
}
// Set up boot context
PortalSessionLifeCycle.bootContext.set(this);
//
try
{
for (Map.Entry<String, String> optionEntry : optionMap.entrySet())
{
ChromatticBuilder.Option<?> option = RECOGNIZED_OPTIONS.get(optionEntry.getKey());
if (option != null)
{
log.debug("Setting Chromattic option " + optionEntry);
builder.setOptionStringValue(option, optionEntry.getValue());
}
}
// Set it now, so we are sure that it will be the correct life cycle
builder.setOptionValue(ChromatticBuilder.SESSION_LIFECYCLE_CLASSNAME, PortalSessionLifeCycle.class.getName());
//
log.debug("Building Chromattic " + domainName);
realChromattic = builder.build();
chromattic = new ChromatticImpl(this);
}
catch (Exception e)
{
log.error("Could not start Chromattic " + domainName, e);
}
finally
{
PortalSessionLifeCycle.bootContext.set(null);
}
}
public final void stop()
{
// Nothing to do for now
}
}