/* * Copyright (c) 2012, 2013, 2015, 2016 Eike Stepper (Berlin, 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 */ package org.eclipse.emf.cdo.examples.client.offline.nodes; import org.eclipse.emf.cdo.common.CDOCommonRepository.State; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.revision.CDORevisionCache; import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; import org.eclipse.emf.cdo.examples.client.offline.Application; import org.eclipse.emf.cdo.examples.company.CompanyPackage; import org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration; import org.eclipse.emf.cdo.net4j.CDONet4jUtil; import org.eclipse.emf.cdo.server.CDOServerBrowser; import org.eclipse.emf.cdo.server.CDOServerUtil; import org.eclipse.emf.cdo.server.IRepository; import org.eclipse.emf.cdo.server.IRepositorySynchronizer; import org.eclipse.emf.cdo.server.IStore; import org.eclipse.emf.cdo.server.ISynchronizableRepository; import org.eclipse.emf.cdo.server.db.CDODBUtil; import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; import org.eclipse.emf.cdo.server.net4j.FailoverAgent; import org.eclipse.emf.cdo.session.CDOSession; import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory; import org.eclipse.emf.cdo.transaction.CDOTransaction; import org.eclipse.emf.cdo.view.CDOAdapterPolicy; import org.eclipse.emf.cdo.view.CDOViewTargetChangedEvent; import org.eclipse.net4j.Net4jUtil; import org.eclipse.net4j.acceptor.IAcceptor; import org.eclipse.net4j.connector.IConnector; import org.eclipse.net4j.db.IDBAdapter; import org.eclipse.net4j.db.IDBConnectionProvider; import org.eclipse.net4j.db.h2.H2Adapter; import org.eclipse.net4j.util.container.IPluginContainer; import org.eclipse.net4j.util.container.SetContainer; import org.eclipse.net4j.util.event.IEvent; import org.eclipse.net4j.util.event.IListener; import org.eclipse.net4j.util.event.ThrowableEventAdapter; import org.eclipse.net4j.util.lifecycle.LifecycleUtil; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.application.IWorkbenchWindowConfigurer; import org.eclipse.wb.swt.ExampleResourceManager; import org.h2.jdbcx.JdbcDataSource; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; /** * @author Eike Stepper */ public abstract class NodeType extends SetContainer<Node> implements IElement { public static final String TYPE_PROPERTY = "Type"; public static final String NAME_PROPERTY = "Name"; public static final String PORT_PROPERTY = "Port"; public static final String SERVER_PROPERTY = "Server"; public static final String MONITOR_PROPERTY = "Monitor"; public static final String BRANCH_PROPERTY = "Branch"; public static final String BROWSER_PROPERTY = "BrowserPort"; private static final String REPOSITORY_NAME = "repository"; private final NodeManager manager; private final List<Property> properties = new ArrayList<Property>(); private final Properties settings = new Properties(); private final Composite[] detailsControls = { null, null }; public NodeType(NodeManager manager) { super(Node.class); this.manager = manager; addProperty(new Property.Entry(this, NAME_PROPERTY)); settings.setProperty(TYPE_PROPERTY, getTypeName()); activate(); } public NodeManager getManager() { return manager; } public List<Property> getProperties() { return properties; } public Properties getSettings() { return settings; } public void showSettings() { showSettings(null); } public void showSettings(Node node) { Composite composite = getDetailsControl(node); Control[] children = composite.getChildren(); Properties settings = node == null ? this.settings : node.getSettings(); for (Property property : properties) { String name = property.getName(); Control control = getControl(children, name); if (control != null) { String value = settings.getProperty(name, ""); property.showSetting(control, value); } } } public void configureWindow(IWorkbenchWindowConfigurer configurer) { configurer.setInitialSize(new Point(800, 500)); configurer.setTitle(Application.NODE.getName()); configurer.setShowCoolBar(false); configurer.setShowMenuBar(false); configurer.setShowStatusLine(false); } private Control getControl(Control[] children, String name) { for (Control control : children) { if (name.equals(control.getData("name"))) { return control; } } return null; } public Image getImage() { return ExampleResourceManager.getPluginImage(Application.PLUGIN_ID, "icons/Folder.gif"); } public Image getInstanceImage() { return ExampleResourceManager.getPluginImage(Application.PLUGIN_ID, "icons/" + getTypeName() + ".gif"); } public String getTypeName() { String name = getClass().getSimpleName(); int lastDot = name.lastIndexOf('.'); if (lastDot != -1) { name = name.substring(lastDot + 1); } return name; } public Composite getDetailsControl() { return getDetailsControl(null); } public Composite getDetailsControl(Node node) { if (node == null) { return detailsControls[0]; } return detailsControls[1]; } public void start(Node node) { IRepository repository = createRepository(node); node.setObject(IRepository.class, repository); IAcceptor acceptor = createAcceptor(node); node.setObject(IAcceptor.class, acceptor); String browserPort = node.getSetting(BROWSER_PROPERTY); if (browserPort != null && browserPort.length() != 0) { CDOServerBrowser browser = (CDOServerBrowser)IPluginContainer.INSTANCE.getElement("org.eclipse.emf.cdo.server.browsers", "default", browserPort); node.setObject(CDOServerBrowser.class, browser); } } public void stop(Node node) { CDOServerBrowser browser = node.getObject(CDOServerBrowser.class); LifecycleUtil.deactivate(browser); IAcceptor acceptor = node.getObject(IAcceptor.class); LifecycleUtil.deactivate(acceptor); IRepository repository = node.getObject(IRepository.class); LifecycleUtil.deactivate(repository); } public Button createUI(final NodeManagerDialog dialog, int kind) { Composite composite = new Composite(dialog.getDetails(), SWT.NONE); composite.setLayout(new GridLayout(2, false)); for (Property property : properties) { property.createField(dialog, composite); } new Label(composite, SWT.NONE); Button button = new Button(composite, SWT.PUSH); button.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); if (kind == NodeManagerDialog.NODE) { button.setText("Delete"); button.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { dialog.onDelete(NodeType.this); } }); composite.setEnabled(false); detailsControls[1] = composite; } else { button.setText("Create"); button.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { dialog.onCreate(NodeType.this); } }); detailsControls[0] = composite; } return button; } protected void addProperty(Property property) { properties.add(property); } @Override protected Node[] sortElements(Node[] array) { Arrays.sort(array); return array; } protected IRepository createRepository(Node node) { JdbcDataSource dataSource = new JdbcDataSource(); dataSource.setURL("jdbc:h2:" + node.getFolder() + "/db/repository"); IMappingStrategy mappingStrategy = CDODBUtil.createHorizontalMappingStrategy(true, true); IDBAdapter dbAdapter = new H2Adapter(); IDBConnectionProvider dbConnectionProvider = dbAdapter.createConnectionProvider(dataSource); IStore store = CDODBUtil.createStore(mappingStrategy, dbAdapter, dbConnectionProvider); Map<String, String> props = new HashMap<String, String>(); props.put(IRepository.Props.OVERRIDE_UUID, REPOSITORY_NAME); props.put(IRepository.Props.SUPPORTING_AUDITS, "true"); props.put(IRepository.Props.SUPPORTING_BRANCHES, "true"); props.put(IRepository.Props.ID_GENERATION_LOCATION, "CLIENT"); IRepository repository = createRepository(node, store, props); repository.setInitialPackages(CompanyPackage.eINSTANCE); activateRepository(repository); return repository; } protected IRepository createRepository(Node node, IStore store, Map<String, String> props) { return CDOServerUtil.createRepository(REPOSITORY_NAME, store, props); } protected void activateRepository(IRepository repository) { // Don't do this with failover participants! CDOServerUtil.addRepository(IPluginContainer.INSTANCE, repository); } protected IAcceptor createAcceptor(Node node) { String description = "0.0.0.0:" + node.getSetting(PORT_PROPERTY); return (IAcceptor)IPluginContainer.INSTANCE.getElement("org.eclipse.net4j.acceptors", "tcp", description); } /** * @author Eike Stepper */ public static abstract class Property { private final NodeType container; private final String name; public Property(NodeType container, String name) { this.container = container; this.name = name; } public NodeType getContainer() { return container; } public String getName() { return name; } public abstract void createField(NodeManagerDialog dialog, Composite parent); public abstract void showSetting(Control control, String value); /** * @author Eike Stepper */ public static abstract class Labelled extends Property { public Labelled(NodeType container, String name) { super(container, name); } @Override public void createField(NodeManagerDialog dialog, Composite parent) { String name = getName(); Label label = new Label(parent, SWT.NONE); label.setText(name + ":"); label.setLayoutData(new GridData(GridData.END)); Control control = createControl(dialog, parent); control.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); control.setData("name", name); } protected abstract Control createControl(NodeManagerDialog dialog, Composite parent); } /** * @author Eike Stepper */ public static class Entry extends Labelled { public Entry(NodeType container, String name) { super(container, name); } @Override protected Control createControl(final NodeManagerDialog dialog, Composite parent) { final Text text = new Text(parent, SWT.BORDER); text.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { if (!dialog.isUpdatingDetails()) { dialog.onModify(Entry.this, text.getText()); } } }); return text; } @Override public void showSetting(Control control, String value) { ((Text)control).setText(value); } } /** * @author Eike Stepper */ public static class Selection extends Labelled { private final Class<? extends NodeType> type; public Selection(NodeType container, String name, Class<? extends NodeType> type) { super(container, name); this.type = type; } public Class<? extends NodeType> getType() { return type; } @Override protected Control createControl(final NodeManagerDialog dialog, Composite parent) { final ComboViewer comboViewer = new ComboViewer(parent, SWT.NONE); comboViewer.setLabelProvider(new LabelProvider()); comboViewer.setContentProvider(new IStructuredContentProvider() { public Object[] getElements(Object inputElement) { List<Node> result = new ArrayList<Node>(); Node[] nodes = getContainer().getManager().getNodes(); for (Node node : nodes) { if (type.isAssignableFrom(node.getType().getClass())) { result.add(node); } } Collections.sort(result); return result.toArray(new Node[result.size()]); } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } public void dispose() { } }); comboViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { if (!dialog.isUpdatingDetails()) { IStructuredSelection selection = (IStructuredSelection)comboViewer.getSelection(); Node node = (Node)selection.getFirstElement(); if (node != null) { dialog.onModify(Selection.this, node.getName()); } } } }); comboViewer.setInput(new Object()); Combo combo = comboViewer.getCombo(); combo.setData("viewer", comboViewer); return combo; } @Override public void showSetting(Control control, String value) { ComboViewer comboViewer = (ComboViewer)control.getData("viewer"); comboViewer.refresh(true); Node node = getContainer().getManager().getNode(value); if (node != null) { comboViewer.setSelection(new StructuredSelection(node)); } else { comboViewer.setSelection(StructuredSelection.EMPTY); } } } } /** * @author Eike Stepper */ public static class Client extends NodeType { public Client(NodeManager manager) { super(manager); addProperty(new Property.Selection(this, SERVER_PROPERTY, Server.class)); addProperty(new Property.Entry(this, BROWSER_PROPERTY)); } @Override public void start(final Node node) { super.start(node); final CDOSession session = (CDOSession)IPluginContainer.INSTANCE.getElement("org.eclipse.emf.cdo.sessions", "cdo", "jvm://example?repositoryName=" + REPOSITORY_NAME); session.getPackageRegistry().putEPackage(CompanyPackage.eINSTANCE); node.setObject(CDOSession.class, session); if (session.getRepositoryInfo().getState() == State.INITIAL) { session.addListener(new IListener() { public void notifyEvent(IEvent event) { if (session.getRepositoryInfo().getState() != State.INITIAL) { session.removeListener(this); createTransaction(node); } } }); } else { createTransaction(node); } } @Override public void stop(Node node) { CDOSession session = node.getObject(CDOSession.class); LifecycleUtil.deactivate(session); super.stop(node); } @Override protected IRepository createRepository(Node node, IStore store, Map<String, String> props) { String serverName = node.getSetting(SERVER_PROPERTY); final Node serverNode = getManager().getNode(serverName); if (serverNode == null) { throw new IllegalStateException("Server not found: " + serverName); } CDOSessionConfigurationFactory factory = new CDOSessionConfigurationFactory() { public CDONet4jSessionConfiguration createSessionConfiguration() { String serverAddress = "localhost:" + serverNode.getSetting(PORT_PROPERTY); CDONet4jSessionConfiguration configuration; if (serverNode.getType() instanceof FailoverMonitor) { configuration = CDONet4jUtil.createFailoverSessionConfiguration(serverAddress, serverNode.getName()); } else { IConnector connector = Net4jUtil.getConnector(IPluginContainer.INSTANCE, "tcp", serverAddress); configuration = CDONet4jUtil.createNet4jSessionConfiguration(); configuration.setConnector(connector); configuration.setRepositoryName(REPOSITORY_NAME); } configuration.setRevisionManager(CDORevisionUtil.createRevisionManager(CDORevisionCache.NOOP)); return configuration; } }; IRepositorySynchronizer synchronizer = CDOServerUtil.createRepositorySynchronizer(factory); synchronizer.setRetryInterval(2); synchronizer.setMaxRecommits(10); synchronizer.setRecommitInterval(2); return CDOServerUtil.createOfflineClone(REPOSITORY_NAME, store, props, synchronizer); } @Override protected IAcceptor createAcceptor(Node node) { return (IAcceptor)IPluginContainer.INSTANCE.getElement("org.eclipse.net4j.acceptors", "jvm", "example"); } protected void createTransaction(final Node node) { CDOSession session = node.getObject(CDOSession.class); String branchPath = node.getSettings().getProperty(BRANCH_PROPERTY, CDOBranch.MAIN_BRANCH_NAME); CDOBranch branch = session.getBranchManager().getBranch(branchPath); if (branch == null) { branch = session.getBranchManager().getMainBranch(); node.getSettings().setProperty(BRANCH_PROPERTY, branch.getPathName()); getManager().saveNode(node); } CDOTransaction transaction = session.openTransaction(branch); transaction.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL); transaction.addListener(new IListener() { public void notifyEvent(IEvent event) { if (event instanceof CDOViewTargetChangedEvent) { CDOViewTargetChangedEvent e = (CDOViewTargetChangedEvent)event; String branchPath = e.getBranchPoint().getBranch().getPathName(); node.getSettings().setProperty(BRANCH_PROPERTY, branchPath); getManager().saveNode(node); } } }); node.setObject(CDOTransaction.class, transaction); } @Override public String toString() { return "Clients"; } } /** * @author Eike Stepper */ public static abstract class Server extends NodeType { public Server(NodeManager manager) { super(manager); addProperty(new Property.Entry(this, PORT_PROPERTY)); } @Override public void configureWindow(IWorkbenchWindowConfigurer configurer) { super.configureWindow(configurer); configurer.setInitialSize(new Point(500, 350)); } public void setConnectedToNetwork(Node node, boolean on) { if (on) { IAcceptor acceptor = createAcceptor(node); node.setObject(IAcceptor.class, acceptor); } else { IAcceptor acceptor = node.getObject(IAcceptor.class); LifecycleUtil.deactivate(acceptor); } } } /** * @author Eike Stepper */ public static abstract class Repository extends Server { public Repository(NodeManager manager) { super(manager); } @Override public String toString() { return "Normal Repositories"; } } /** * @author Eike Stepper */ public static class NormalRepository extends Repository { public NormalRepository(NodeManager manager) { super(manager); addProperty(new Property.Entry(this, BROWSER_PROPERTY)); } @Override public String toString() { return "Normal Repositories"; } } /** * @author Eike Stepper */ public static class FailoverRepository extends Repository { private FailoverAgent agent; public FailoverRepository(NodeManager manager) { super(manager); addProperty(new Property.Selection(this, MONITOR_PROPERTY, FailoverMonitor.class)); addProperty(new Property.Entry(this, BROWSER_PROPERTY)); } @Override protected IRepository createRepository(Node node, IStore store, Map<String, String> props) { String monitorName = node.getSetting(MONITOR_PROPERTY); Node monitorNode = getManager().getNode(monitorName); if (monitorNode == null) { throw new IllegalStateException("Monitor not found: " + monitorName); } final String monitorAddress = "localhost:" + monitorNode.getSetting(PORT_PROPERTY); ISynchronizableRepository repository = CDOServerUtil.createFailoverParticipant(REPOSITORY_NAME, store, props); agent = new FailoverAgent() { @Override protected IRepositorySynchronizer createRepositorySynchronizer() { IRepositorySynchronizer synchronizer = super.createRepositorySynchronizer(); synchronizer.addListener(ThrowableEventAdapter.ToPrintStream.CONSOLE); return synchronizer; } @Override protected CDONet4jSessionConfiguration createSessionConfiguration(String connectorDescription, String repositoryName) { IConnector connector = Net4jUtil.getConnector(IPluginContainer.INSTANCE, "tcp", connectorDescription); CDONet4jSessionConfiguration configuration = CDONet4jUtil.createNet4jSessionConfiguration(); configuration.setConnector(connector); configuration.setRepositoryName(repositoryName); configuration.setRevisionManager(CDORevisionUtil.createRevisionManager(CDORevisionCache.NOOP)); return configuration; } }; agent.setMonitorConnector(Net4jUtil.getConnector(IPluginContainer.INSTANCE, "tcp", monitorAddress)); agent.setConnectorDescription("localhost:" + node.getSetting(PORT_PROPERTY)); agent.setRepository(repository); agent.setGroup(monitorNode.getName()); agent.setRate(1500L); agent.setTimeout(3000L); return repository; } @Override protected void activateRepository(IRepository repository) { agent.activate(); } @Override public String toString() { return "Failover Repositories"; } } /** * @author Eike Stepper */ public static class FailoverMonitor extends Server { public FailoverMonitor(NodeManager manager) { super(manager); } @Override public void start(Node node) { super.start(node); org.eclipse.emf.cdo.server.net4j.FailoverMonitor monitor = (org.eclipse.emf.cdo.server.net4j.FailoverMonitor)IPluginContainer.INSTANCE.getElement( org.eclipse.emf.cdo.server.net4j.FailoverMonitor.PRODUCT_GROUP, org.eclipse.emf.cdo.server.net4j.FailoverMonitor.Factory.TYPE, node.getName()); node.setObject(org.eclipse.emf.cdo.server.net4j.FailoverMonitor.class, monitor); } @Override public void stop(Node node) { org.eclipse.emf.cdo.server.net4j.FailoverMonitor monitor = node.getObject(org.eclipse.emf.cdo.server.net4j.FailoverMonitor.class); LifecycleUtil.deactivate(monitor); super.stop(node); } @Override protected IRepository createRepository(Node node) { return null; } @Override public String toString() { return "Failover Monitors"; } } }