/*
* JBoss, a division of Red Hat
* Copyright 2011, Red Hat Middleware, LLC, and individual
* contributors as indicated by the @authors tag. See the
* copyright.txt in the distribution for a full listing of
* individual contributors.
*
* 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.gatein.wsrp.jcr;
import org.chromattic.api.Chromattic;
import org.chromattic.api.ChromatticBuilder;
import org.chromattic.api.ChromatticSession;
import org.gatein.common.util.ParameterValidation;
import org.gatein.wsrp.SupportsLastModified;
import org.gatein.wsrp.jcr.mapping.BaseMapping;
import org.gatein.wsrp.jcr.mapping.mixins.LastModified;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Base class for ChromatticPersister implementations.
*
* @author <a href="mailto:chris.laprun@jboss.com">Chris Laprun</a>
* @version $Revision$
*/
public abstract class BaseChromatticPersister implements ChromatticPersister
{
private Chromattic chrome;
// todo: these constants are used in the GateIn integration and maybe should be moved there.
public static final String WSRP_WORKSPACE_NAME = "wsrp-system";
public static final String PORTLET_STATES_WORKSPACE_NAME = "pc-system";
protected static final String REPOSITORY_NAME = "repository";
protected String workspaceName;
/** Records which Java class maps to which mapping class. */
private Map<Class, Class<? extends BaseMapping>> modelToMapping;
/** Holds thread-specific ChromatticSessions. */
private ThreadLocal<ChromatticSession> sessionHolder = new ThreadLocal<ChromatticSession>();
public BaseChromatticPersister(String workspaceName)
{
this.workspaceName = workspaceName;
}
/**
* Initializes Chromattic with the specified mapping classes. Mapping classes convert from a JCR representation to an object representation of the persisted data and back.
*
* @param mappingClasses the list of mapping classes this ChromatticPersister will be able to deal with.
* @throws Exception
*/
public void initializeBuilderFor(List<Class> mappingClasses) throws Exception
{
ChromatticBuilder builder = ChromatticBuilder.create();
builder.setOptionValue(ChromatticBuilder.INSTRUMENTOR_CLASSNAME, "org.chromattic.apt.InstrumentorImpl");
//let subclasses set their own options for the builder
setBuilderOptions(builder);
// initialize class to mapping map
modelToMapping = new HashMap<Class, Class<? extends BaseMapping>>(mappingClasses.size());
for (Class mappingClass : mappingClasses)
{
// if we're passing a BaseMapping, extract the first generic type which corresponds to the model class the BaseMapping deals with
if (BaseMapping.class.isAssignableFrom(mappingClass))
{
Type[] interfaces = mappingClass.getGenericInterfaces();
if (ParameterValidation.existsAndIsNotEmpty(interfaces))
{
Class type = (Class)((ParameterizedType)interfaces[0]).getActualTypeArguments()[0];
modelToMapping.put(type, mappingClass);
}
}
builder.add(mappingClass);
}
chrome = builder.build();
}
protected abstract void setBuilderOptions(ChromatticBuilder builder);
public ChromatticSession getSession()
{
ChromatticSession chromatticSession = sessionHolder.get();
if (chromatticSession == null)
{
ChromatticSession session = chrome.openSession();
sessionHolder.set(session);
return session;
}
else
{
return chromatticSession;
}
}
public void closeSession(boolean save)
{
ChromatticSession session = getOpenedSessionOrFail();
if (save)
{
synchronized (this)
{
session.save();
}
}
session.close();
sessionHolder.set(null);
}
@Override
public boolean isSessionClosed()
{
final ChromatticSession session = sessionHolder.get();
return session == null || session.isClosed();
}
private ChromatticSession getOpenedSessionOrFail()
{
ChromatticSession session = sessionHolder.get();
if (session == null)
{
throw new IllegalStateException("Cannot close the session as it hasn't been opened first!");
}
return session;
}
public synchronized void save()
{
getOpenedSessionOrFail().save();
}
public <T> boolean delete(T toDelete, StoresByPathManager<T> manager)
{
Class<?> modelClass = toDelete.getClass();
Class<? extends BaseMapping> baseMappingClass = modelToMapping.get(modelClass);
if (baseMappingClass == null)
{
throw new IllegalArgumentException("Cannot find a mapping class for " + modelClass.getName());
}
ChromatticSession session = getSession();
Object old = session.findByPath(baseMappingClass, manager.getChildPath(toDelete));
if (old != null)
{
session.remove(old);
// update last modified of element linked to toDelete if needed
final LastModified lastModified = manager.lastModifiedToUpdateOnDelete(session);
if (lastModified != null)
{
lastModified.setLastModified(SupportsLastModified.now());
}
closeSession(true);
return true;
}
else
{
closeSession(false);
return false;
}
}
}