/*
* 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.services.jcr.impl;
import org.exoplatform.commons.utils.SecurityHelper;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.component.ComponentPlugin;
import org.exoplatform.container.component.ThreadContext;
import org.exoplatform.container.component.ThreadContextHolder;
import org.exoplatform.container.configuration.ConfigurationManager;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
import org.exoplatform.services.jcr.config.RepositoryEntry;
import org.exoplatform.services.jcr.config.RepositoryServiceConfiguration;
import org.exoplatform.services.jcr.config.WorkspaceEntry;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.core.nodetype.ExtendedNodeTypeManager;
import org.exoplatform.services.jcr.core.nodetype.NodeTypeDataManager;
import org.exoplatform.services.jcr.core.security.JCRRuntimePermissions;
import org.exoplatform.services.jcr.impl.core.RepositoryImpl;
import org.exoplatform.services.jcr.impl.core.SessionRegistry;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.picocontainer.Startable;
import java.io.InputStream;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import javax.jcr.RepositoryException;
/**
* Created by The eXo Platform SAS.
*
* @author <a href="mailto:geaz@users.sourceforge.net">Gennady Azarenkov </a>
* @version $Id: RepositoryServiceImpl.java 13986 2008-05-08 10:48:43Z pnedonosko $
*/
public class RepositoryServiceImpl implements RepositoryService, Startable, ThreadContextHolder
{
protected static Log log = ExoLogger.getLogger("exo.jcr.component.core.RepositoryServiceImpl");
private final RepositoryServiceConfiguration config;
private final ThreadLocal<String> currentRepositoryName = new ThreadLocal<String>();
private final ConcurrentHashMap<String, RepositoryContainer> repositoryContainers =
new ConcurrentHashMap<String, RepositoryContainer>();
private final List<ComponentPlugin> addNodeTypePlugins;
private final List<ComponentPlugin> addNamespacesPlugins;
private final ExoContainerContext containerContext;
private ExoContainer parentContainer;
public RepositoryServiceImpl(RepositoryServiceConfiguration configuration)
{
this(configuration, null);
}
public RepositoryServiceImpl(RepositoryServiceConfiguration configuration, ExoContainerContext context)
{
this(configuration, context, null);
}
/**
* @param synchronizer This component is used to synchronize the creation of the repositories between
* all the cluster nodes. If this component has been defined in the configuration, it has to
* be started before the {@link RepositoryServiceImpl} so we have to enforce the dependency
* with this component by adding it to the constructor
*/
public RepositoryServiceImpl(RepositoryServiceConfiguration configuration, ExoContainerContext context,
RepositoryCreationSynchronizer synchronizer)
{
this.config = configuration;
addNodeTypePlugins = new ArrayList<ComponentPlugin>();
addNamespacesPlugins = new ArrayList<ComponentPlugin>();
containerContext = context;
currentRepositoryName.set(config.getDefaultRepositoryName());
}
public void addPlugin(ComponentPlugin plugin)
{
if (plugin instanceof AddNodeTypePlugin)
{
addNodeTypePlugins.add(plugin);
}
else if (plugin instanceof AddNamespacesPlugin)
{
addNamespacesPlugins.add(plugin);
}
}
/**
* Create repository. <br>
* Init worksapces for initial start or them load from persistence. <br>
* Add namespaces and nodetypes from service plugins.
*
*/
public void createRepository(RepositoryEntry rEntry) throws RepositoryConfigurationException, RepositoryException
{
// Need privileges to manage repository.
SecurityManager security = System.getSecurityManager();
if (security != null)
{
security.checkPermission(JCRRuntimePermissions.MANAGE_REPOSITORY_PERMISSION);
}
if (repositoryContainers.containsKey(rEntry.getName()))
{
throw new RepositoryConfigurationException("Repository container " + rEntry.getName() + " already started");
}
final RepositoryContainer repositoryContainer =
new RepositoryContainer(parentContainer, rEntry, addNamespacesPlugins);
// Storing and starting the repository container under
// key=repository_name
try
{
if (repositoryContainers.putIfAbsent(rEntry.getName(), repositoryContainer) == null)
{
SecurityHelper.doPrivilegedAction(new PrivilegedAction<Void>()
{
public Void run()
{
repositoryContainer.start();
return null;
}
});
}
else
{
throw new RepositoryConfigurationException("Repository container " + rEntry.getName() + " already started");
}
}
catch (Throwable t) //NOSONAR
{
repositoryContainers.remove(rEntry.getName());
throw new RepositoryConfigurationException("Repository container " + rEntry.getName() + " was not started.", t);
}
if (!config.getRepositoryConfigurations().contains(rEntry))
{
config.getRepositoryConfigurations().add(rEntry);
}
registerNodeTypes(rEntry.getName());
// turn on Repository ONLINE
ManageableRepository mr =
(ManageableRepository)repositoryContainer.getComponentInstanceOfType(ManageableRepository.class);
mr.setState(ManageableRepository.ONLINE);
}
public RepositoryServiceConfiguration getConfig()
{
// Need privileges to manage repository.
SecurityManager security = System.getSecurityManager();
if (security != null)
{
security.checkPermission(JCRRuntimePermissions.MANAGE_REPOSITORY_PERMISSION);
}
return config;
}
/**
* {@inheritDoc}
*/
public ThreadContext getThreadContext()
{
return new ThreadContext(currentRepositoryName);
}
/**
* @return Name of current repository if exists or null in other case
*/
public String getCurrentRepositoryName()
{
return currentRepositoryName.get();
}
public ManageableRepository getCurrentRepository() throws RepositoryException
{
if (currentRepositoryName.get() == null)
{
return getDefaultRepository();
}
return getRepository(currentRepositoryName.get());
}
public ManageableRepository getDefaultRepository() throws RepositoryException
{
return getRepository(config.getDefaultRepositoryName());
}
// ------------------- Startable ----------------------------
public ManageableRepository getRepository(String name) throws RepositoryException
{
RepositoryContainer repositoryContainer = repositoryContainers.get(name);
log.debug("RepositoryServiceimpl() getRepository " + name);
if (repositoryContainer == null)
{
throw new RepositoryException("Repository '" + name + "' not found.");
}
return (ManageableRepository)repositoryContainer.getComponentInstanceOfType(ManageableRepository.class);
}
public RepositoryContainer getRepositoryContainer(String name)
{
return repositoryContainers.get(name);
}
public void setCurrentRepositoryName(String repositoryName) throws RepositoryConfigurationException
{
// Need privileges to manage repository.
SecurityManager security = System.getSecurityManager();
if (security != null)
{
security.checkPermission(JCRRuntimePermissions.MANAGE_REPOSITORY_PERMISSION);
}
if (!repositoryContainers.containsKey(repositoryName))
{
throw new RepositoryConfigurationException("Repository is not configured. Name " + repositoryName);
}
currentRepositoryName.set(repositoryName);
}
public void start()
{
// Need privileges to manage repository.
SecurityManager security = System.getSecurityManager();
if (security != null)
{
security.checkPermission(JCRRuntimePermissions.MANAGE_REPOSITORY_PERMISSION);
}
try
{
ExoContainer container = null;
if (containerContext == null)
{
container = PortalContainer.getInstance();
}
else
{
container = containerContext.getContainer();
}
init(container);
}
catch (RepositoryException e)
{
log.error("Error start repository service", e);
}
catch (RepositoryConfigurationException e)
{
log.error("Error start repository service", e);
}
catch (Throwable e) //NOSONAR
{
log.error("Error start repository service", e);
throw new RuntimeException(e);
}
}
public void stop()
{
// Need privileges to manage repository.
SecurityManager security = System.getSecurityManager();
if (security != null)
{
security.checkPermission(JCRRuntimePermissions.MANAGE_REPOSITORY_PERMISSION);
}
for (Entry<String, RepositoryContainer> entry : repositoryContainers.entrySet())
{
entry.getValue().stop();
}
repositoryContainers.clear();
addNamespacesPlugins.clear();
addNodeTypePlugins.clear();
}
private void init(ExoContainer container) throws RepositoryConfigurationException, RepositoryException
{
this.parentContainer = container;
List<RepositoryEntry> rEntries = config.getRepositoryConfigurations();
for (int i = 0; i < rEntries.size(); i++)
{
RepositoryEntry rEntry = rEntries.get(i);
// Making new repository container as portal's subcontainer
createRepository(rEntry);
}
}
private void registerNodeTypes(String repositoryName) throws RepositoryException
{
ConfigurationManager configService =
(ConfigurationManager)parentContainer.getComponentInstanceOfType(ConfigurationManager.class);
ExtendedNodeTypeManager ntManager = getRepository(repositoryName).getNodeTypeManager();
//
for (int j = 0; j < addNodeTypePlugins.size(); j++)
{
AddNodeTypePlugin plugin = (AddNodeTypePlugin)addNodeTypePlugins.get(j);
List<String> autoNodeTypesFiles = plugin.getNodeTypesFiles(AddNodeTypePlugin.AUTO_CREATED);
if (autoNodeTypesFiles != null && autoNodeTypesFiles.size() > 0)
{
for (String nodeTypeFilesName : autoNodeTypesFiles)
{
InputStream inXml;
try
{
inXml = configService.getInputStream(nodeTypeFilesName);
}
catch (Exception e)
{
throw new RepositoryException(e);
}
if (log.isDebugEnabled())
{
log.debug("Trying register node types from xml-file " + nodeTypeFilesName);
}
ntManager.registerNodeTypes(inXml, ExtendedNodeTypeManager.IGNORE_IF_EXISTS,
NodeTypeDataManager.TEXT_XML);
if (log.isDebugEnabled())
{
log.debug("Node types is registered from xml-file " + nodeTypeFilesName);
}
}
List<String> defaultNodeTypesFiles = plugin.getNodeTypesFiles(repositoryName);
if (defaultNodeTypesFiles != null && defaultNodeTypesFiles.size() > 0)
{
for (String nodeTypeFilesName : defaultNodeTypesFiles)
{
InputStream inXml;
try
{
inXml = configService.getInputStream(nodeTypeFilesName);
}
catch (Exception e)
{
throw new RepositoryException(e);
}
log.info("Trying register node types (" + repositoryName + ") from xml-file " + nodeTypeFilesName);
ntManager.registerNodeTypes(inXml, ExtendedNodeTypeManager.IGNORE_IF_EXISTS,
NodeTypeDataManager.TEXT_XML);
log.info("Node types is registered (" + repositoryName + ") from xml-file " + nodeTypeFilesName);
}
}
}
}
}
/**
* {@inheritDoc}
*/
public void removeRepository(String name) throws RepositoryException
{
this.removeRepository(name, false);
}
/**
* {@inheritDoc}
*/
public void removeRepository(final String name, boolean forceRemove) throws RepositoryException
{
// Need privileges to manage repository.
SecurityManager security = System.getSecurityManager();
if (security != null)
{
security.checkPermission(JCRRuntimePermissions.MANAGE_REPOSITORY_PERMISSION);
}
if (!forceRemove && !canRemoveRepository(name))
{
throw new RepositoryException("Repository " + name + " in use. If you want to "
+ " remove repository close all open sessions");
}
try
{
final RepositoryEntry repconfig = config.getRepositoryConfiguration(name);
final RepositoryImpl repo = (RepositoryImpl)getRepository(name);
repo.setState(ManageableRepository.OFFLINE);
SecurityHelper.doPrivilegedExceptionAction(new PrivilegedExceptionAction<Void>()
{
public Void run() throws RepositoryException
{
List<WorkspaceEntry> workspaces = new ArrayList<WorkspaceEntry>(repconfig.getWorkspaceEntries());
for (WorkspaceEntry entry : workspaces)
{
repo.internalRemoveWorkspace(entry.getName());
}
RepositoryContainer repositoryContainer = repositoryContainers.get(name);
repositoryContainer.stop();
repositoryContainers.remove(name);
config.getRepositoryConfigurations().remove(repconfig);
parentContainer.unregisterComponent(repositoryContainer.getName());
return null;
}
});
}
catch (PrivilegedActionException e)
{
Throwable cause = e.getCause();
if (cause instanceof RepositoryException)
{
throw (RepositoryException)cause;
}
else
{
throw new RepositoryException(cause);
}
}
catch (RepositoryConfigurationException e)
{
throw new RepositoryException(e);
}
catch (Exception e)
{
throw new RepositoryException(e);
}
}
/**
* {@inheritDoc}
*/
public boolean canRemoveRepository(String name) throws RepositoryException
{
RepositoryImpl repo = (RepositoryImpl)getRepository(name);
try
{
RepositoryEntry repconfig = config.getRepositoryConfiguration(name);
for (WorkspaceEntry wsEntry : repconfig.getWorkspaceEntries())
{
// Check non system workspaces
if (!repo.getSystemWorkspaceName().equals(wsEntry.getName()) && !repo.canRemoveWorkspace(wsEntry.getName()))
{
return false;
}
}
// check system workspace
RepositoryContainer repositoryContainer = repositoryContainers.get(name);
SessionRegistry sessionRegistry =
(SessionRegistry)repositoryContainer.getComponentInstance(SessionRegistry.class);
if (sessionRegistry == null || sessionRegistry.isInUse(repo.getSystemWorkspaceName()))
{
return false;
}
}
catch (RepositoryConfigurationException e)
{
throw new RepositoryException(e);
}
return true;
}
}