blob: e2d756e579804bf98e2f3d20d6a5829c30e1abcd [file] [log] [blame]
/*
* Copyright (c) 2012, 2013, 2015, 2016, 2019, 2020 Eike Stepper (Loehne, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
* Christian W. Damus (CEA LIST) - bug 418454
*/
package org.eclipse.emf.cdo.server.internal.admin;
import org.eclipse.emf.cdo.common.CDOCommonRepository.State;
import org.eclipse.emf.cdo.common.CDOCommonRepository.Type;
import org.eclipse.emf.cdo.server.CDOServerUtil;
import org.eclipse.emf.cdo.server.IRepository;
import org.eclipse.emf.cdo.server.ISession;
import org.eclipse.emf.cdo.server.internal.admin.bundle.OM;
import org.eclipse.emf.cdo.server.internal.admin.protocol.CDOAdminServerProtocol;
import org.eclipse.emf.cdo.server.spi.admin.CDOAdminHandler;
import org.eclipse.emf.cdo.server.spi.admin.CDOAdminHandler2;
import org.eclipse.emf.cdo.spi.common.admin.AbstractCDOAdmin;
import org.eclipse.emf.cdo.spi.server.AuthenticationUtil;
import org.eclipse.emf.cdo.spi.server.IAuthenticationProtocol;
import org.eclipse.emf.cdo.spi.server.ISessionProtocol;
import org.eclipse.emf.cdo.spi.server.RepositoryFactory;
import org.eclipse.net4j.channel.IChannel;
import org.eclipse.net4j.jvm.IJVMChannel;
import org.eclipse.net4j.signal.ISignalProtocol;
import org.eclipse.net4j.util.confirmation.Confirmation;
import org.eclipse.net4j.util.container.ContainerEventAdapter;
import org.eclipse.net4j.util.container.IContainer;
import org.eclipse.net4j.util.container.IManagedContainer;
import org.eclipse.net4j.util.container.IManagedContainerProvider;
import org.eclipse.net4j.util.container.IPluginContainer;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.lifecycle.ILifecycle;
import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.spi.net4j.Protocol;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author Eike Stepper
*/
public class CDOAdminServer extends AbstractCDOAdmin implements IManagedContainerProvider
{
public static final String PROP_IGNORE = "org.eclipse.emf.cdo.server.admin.ignore";
private static final String TRUE = Boolean.TRUE.toString();
private static Class<?> IJVMCHANNEL_CLASS;
private final IManagedContainer container;
private final IListener containerListener = new ContainerEventAdapter<Object>(true)
{
@Override
protected void onAdded(IContainer<Object> container, Object element)
{
if (element instanceof IRepository)
{
IRepository repository = (IRepository)element;
repositoryAdded(repository);
}
}
@Override
protected void onRemoved(IContainer<Object> container, Object element)
{
if (element instanceof IRepository)
{
IRepository repository = (IRepository)element;
repositoryRemoved(repository);
}
}
};
private final Set<CDOAdminServerProtocol> protocols = new HashSet<>();
public CDOAdminServer(IManagedContainer container, long timeout)
{
super(timeout);
this.container = container;
}
@Override
public final IManagedContainer getContainer()
{
return container;
}
public void registerProtocol(final CDOAdminServerProtocol protocol)
{
protocol.addListener(new LifecycleEventAdapter()
{
@Override
protected void onDeactivated(ILifecycle lifecycle)
{
protocol.deactivate();
}
});
synchronized (protocols)
{
protocols.add(protocol);
}
}
public void deregisterProtocol(CDOAdminServerProtocol protocol)
{
synchronized (protocols)
{
protocols.remove(protocol);
}
}
@Override
protected boolean doCreateRepository(String name, String type, Map<String, Object> properties)
{
CDOAdminHandler handler = getAdminHandler(type);
if (handler instanceof CDOAdminHandler2)
{
// Must provide administrator credentials to create a repository
((CDOAdminHandler2)handler).authenticateAdministrator();
}
IRepository delegate = handler.createRepository(name, properties);
CDOServerUtil.addRepository(container, delegate);
return true;
}
@Override
protected boolean doDeleteRepository(String name, String type)
{
CDOAdminHandler handler = getAdminHandler(type);
CDOAdminServerRepository repository = (CDOAdminServerRepository)getRepository(name);
if (repository == null)
{
return false;
}
IRepository delegate = repository.getDelegate();
// Can this handler delete the repository?
if (!canDelete(delegate, handler))
{
throw new IllegalStateException("The repository is permanently configured.");
}
if (handler instanceof CDOAdminHandler2)
{
// Must provide administrator credentials to delete a repository
((CDOAdminHandler2)handler).authenticateAdministrator();
}
// Do we have any connected users? If so, prompt for confirmation
if (hasConnections(delegate) && !confirmDeletion(delegate))
{
return false;
}
LifecycleUtil.deactivate(delegate);
handler.deleteRepository(delegate);
return true;
}
protected boolean canDelete(IRepository repository, CDOAdminHandler handler)
{
return !(handler instanceof CDOAdminHandler2) || ((CDOAdminHandler2)handler).canDelete(repository);
}
protected boolean hasConnections(IRepository delegate)
{
ISession[] sessions = delegate.getSessionManager().getSessions();
if (sessions != null)
{
for (int i = 0; i < sessions.length; i++)
{
ISessionProtocol protocol = sessions[i].getProtocol();
if (protocol instanceof Protocol<?>)
{
// Connections from within this JVM do not count
IChannel channel = ((Protocol<?>)protocol).getChannel();
if (channel != null && !isJVMChannel(channel))
{
return true;
}
}
}
}
return false;
}
protected boolean confirmDeletion(IRepository delegate)
{
IAuthenticationProtocol authProtocol = AuthenticationUtil.getAuthenticationProtocol();
if (authProtocol instanceof CDOAdminServerProtocol)
{
CDOAdminServerProtocol protocol = (CDOAdminServerProtocol)authProtocol;
String message = MessageFormat.format("The repository \"{0}\" has connected users. Proceed with deletion anyways?", delegate.getName());
try
{
if (protocol.sendConfirmationRequest("Repository In Use", message, Confirmation.NO, Confirmation.YES, Confirmation.NO) != Confirmation.YES)
{
return false;
}
}
catch (Exception ex)
{
OM.LOG.error(ex);
return false;
}
}
return true;
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
for (Object element : container.getElements(RepositoryFactory.PRODUCT_GROUP))
{
if (element instanceof IRepository)
{
addDelegate((IRepository)element);
}
}
container.addListener(containerListener);
}
@Override
protected void doDeactivate() throws Exception
{
container.removeListener(containerListener);
super.doDeactivate();
}
protected boolean validateDelegate(IRepository delegate)
{
String ignore = delegate.getProperties().get(PROP_IGNORE);
if (TRUE.equalsIgnoreCase(ignore))
{
return false;
}
return true;
}
protected CDOAdminServerRepository addDelegate(IRepository delegate)
{
if (validateDelegate(delegate))
{
CDOAdminServerRepository repository = new CDOAdminServerRepository(this, delegate);
if (addElement(repository))
{
return repository;
}
}
return null;
}
protected void repositoryAdded(IRepository delegate)
{
CDOAdminServerRepository repository = addDelegate(delegate);
if (repository != null)
{
for (CDOAdminServerProtocol protocol : getProtocols())
{
try
{
protocol.sendRepositoryAdded(repository);
}
catch (Exception ex)
{
handleNotificationProblem(protocol, ex);
}
}
}
}
protected void repositoryRemoved(IRepository delegate)
{
String name = delegate.getName();
CDOAdminServerRepository repository = (CDOAdminServerRepository)getRepository(name);
if (repository != null && removeElement(repository))
{
repository.dispose();
for (CDOAdminServerProtocol protocol : getProtocols())
{
try
{
protocol.sendRepositoryRemoved(name);
}
catch (Exception ex)
{
handleNotificationProblem(protocol, ex);
}
}
}
}
protected void repositoryTypeChanged(String name, Type oldType, Type newType)
{
for (CDOAdminServerProtocol protocol : getProtocols())
{
try
{
protocol.sendRepositoryTypeChanged(name, oldType, newType);
}
catch (Exception ex)
{
handleNotificationProblem(protocol, ex);
}
}
}
protected void repositoryStateChanged(String name, State oldState, State newState)
{
for (CDOAdminServerProtocol protocol : getProtocols())
{
try
{
protocol.sendRepositoryStateChanged(name, oldState, newState);
}
catch (Exception ex)
{
handleNotificationProblem(protocol, ex);
}
}
}
protected void repositoryReplicationProgressed(String name, double totalWork, double work)
{
for (CDOAdminServerProtocol protocol : getProtocols())
{
try
{
protocol.sendRepositoryReplicationProgressed(name, totalWork, work);
}
catch (Exception ex)
{
handleNotificationProblem(protocol, ex);
}
}
}
protected void handleNotificationProblem(CDOAdminServerProtocol protocol, Exception ex)
{
OM.LOG.warn("A problem occured while notifying client " + protocol, ex);
}
protected CDOAdminHandler getAdminHandler(String type)
{
return (CDOAdminHandler)container.getElement(CDOAdminHandler.Factory.PRODUCT_GROUP, type, null);
}
private CDOAdminServerProtocol[] getProtocols()
{
synchronized (protocols)
{
return protocols.toArray(new CDOAdminServerProtocol[protocols.size()]);
}
}
private static boolean isJVMChannel(IChannel channel)
{
return getIJVMChannelClass().isInstance(channel);
}
private static Class<?> getIJVMChannelClass()
{
if (IJVMCHANNEL_CLASS == null)
{
// Try to load the class, but the plug-in may not be installed
try
{
new Runnable()
{
@Override
public void run()
{
IJVMCHANNEL_CLASS = IJVMChannel.class;
}
}.run();
}
catch (LinkageError er)
{
// The class is not available, so no channel can be an IJVMChannel
IJVMCHANNEL_CLASS = Void.class;
}
}
return IJVMCHANNEL_CLASS;
}
/**
* @author Eike Stepper
*/
public static class Factory extends org.eclipse.net4j.util.factory.Factory implements IManagedContainerProvider
{
public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.admin.adminServers";
public static final String TYPE = "default";
private final IManagedContainer container;
private final long timeout;
public Factory(IManagedContainer container)
{
this(container, ISignalProtocol.DEFAULT_TIMEOUT);
}
public Factory(IManagedContainer container, long timeout)
{
super(PRODUCT_GROUP, TYPE);
this.container = container;
this.timeout = timeout;
}
@Override
public final IManagedContainer getContainer()
{
return container;
}
public final long getTimeout()
{
return timeout;
}
@Override
public CDOAdminServer create(String description)
{
return new CDOAdminServer(container, timeout);
}
public static CDOAdminServer get(IManagedContainer container, String description)
{
return (CDOAdminServer)container.getElement(PRODUCT_GROUP, TYPE, description);
}
/**
* @author Eike Stepper
*/
public static final class Plugin extends Factory
{
public Plugin()
{
super(IPluginContainer.INSTANCE);
}
}
}
}