diff options
author | Eike Stepper | 2018-05-20 07:39:45 +0000 |
---|---|---|
committer | Eike Stepper | 2018-05-20 07:39:45 +0000 |
commit | 58168ee30078b21e11cb1e11267c2b1dfa3787c1 (patch) | |
tree | 0784dbfaea772242bfc0126214c3c690491d2faa /plugins | |
parent | 510ff43670eab4a25a23e99dc503476c5a398b81 (diff) | |
download | cdo-58168ee30078b21e11cb1e11267c2b1dfa3787c1.tar.gz cdo-58168ee30078b21e11cb1e11267c2b1dfa3787c1.tar.xz cdo-58168ee30078b21e11cb1e11267c2b1dfa3787c1.zip |
[534898] Provide a repository activity logbuffer-trace
https://bugs.eclipse.org/bugs/show_bug.cgi?id=534898
Diffstat (limited to 'plugins')
16 files changed, 1060 insertions, 6 deletions
diff --git a/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml b/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml index 1936e4bc5e..e00f297083 100644 --- a/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml +++ b/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml @@ -42,6 +42,13 @@ <initialPackage nsURI="http://www.eclipse.org/emf/CDO/examples/company/1.0.0"/> --> + <!-- Example http://bugs.eclipse.org/534898 + <activityLog type="rolling"> + <property name="file" value="/develop/cdo-master/repo1-activities"/> + <property name="size" value="100000000"/> + </activityLog> + --> + <store type="db"> <!-- Example http://bugs.eclipse.org/396379 (if idGenerationLocation == CLIENT) diff --git a/plugins/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml b/plugins/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml index 1936e4bc5e..e00f297083 100644 --- a/plugins/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml +++ b/plugins/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml @@ -42,6 +42,13 @@ <initialPackage nsURI="http://www.eclipse.org/emf/CDO/examples/company/1.0.0"/> --> + <!-- Example http://bugs.eclipse.org/534898 + <activityLog type="rolling"> + <property name="file" value="/develop/cdo-master/repo1-activities"/> + <property name="size" value="100000000"/> + </activityLog> + --> + <store type="db"> <!-- Example http://bugs.eclipse.org/396379 (if idGenerationLocation == CLIENT) diff --git a/plugins/org.eclipse.emf.cdo.server.admin/src/org/eclipse/emf/cdo/server/internal/admin/RepositoryConfigurationManagerExtension.java b/plugins/org.eclipse.emf.cdo.server.admin/src/org/eclipse/emf/cdo/server/internal/admin/RepositoryConfigurationManagerExtension.java index ac9536b211..daccf819fb 100644 --- a/plugins/org.eclipse.emf.cdo.server.admin/src/org/eclipse/emf/cdo/server/internal/admin/RepositoryConfigurationManagerExtension.java +++ b/plugins/org.eclipse.emf.cdo.server.admin/src/org/eclipse/emf/cdo/server/internal/admin/RepositoryConfigurationManagerExtension.java @@ -128,6 +128,7 @@ public class RepositoryConfigurationManagerExtension implements IAppExtension .getElement(CDORepositoryConfigurationManager.Factory.PRODUCT_GROUP, type, description); repoManager.setAdminRepository(repository); + OM.LOG.info("Admin repository: " + repository.getName()); return repoManager; } diff --git a/plugins/org.eclipse.emf.cdo.server.product/config/cdo-server.xml b/plugins/org.eclipse.emf.cdo.server.product/config/cdo-server.xml index 9ae1abb2d7..9b8209e539 100644 --- a/plugins/org.eclipse.emf.cdo.server.product/config/cdo-server.xml +++ b/plugins/org.eclipse.emf.cdo.server.product/config/cdo-server.xml @@ -42,6 +42,13 @@ <initialPackage nsURI="http://www.eclipse.org/emf/CDO/examples/company/1.0.0"/> --> + <!-- Example http://bugs.eclipse.org/534898 + <activityLog type="rolling"> + <property name="file" value="/develop/cdo-master/repo1-activities"/> + <property name="size" value="100000000"/> + </activityLog> + --> + <store type="db"> <!-- Example http://bugs.eclipse.org/396379 (if idGenerationLocation == CLIENT) diff --git a/plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf/cdo/server/internal/security/SecurityExtension.java b/plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf/cdo/server/internal/security/SecurityExtension.java index 49c23fe642..6618dfe40f 100644 --- a/plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf/cdo/server/internal/security/SecurityExtension.java +++ b/plugins/org.eclipse.emf.cdo.server.security/src/org/eclipse/emf/cdo/server/internal/security/SecurityExtension.java @@ -128,7 +128,10 @@ public class SecurityExtension implements IAppExtension2 } String qualifiedDescription = String.format("%s:%s", name, description); //$NON-NLS-1$ - container.getElement(SecurityManagerFactory.PRODUCT_GROUP, type, qualifiedDescription); + if (container.getElement(SecurityManagerFactory.PRODUCT_GROUP, type, qualifiedDescription) != null) + { + OM.LOG.info("Security manager for repository " + repository.getName() + ": " + qualifiedDescription); + } } } diff --git a/plugins/org.eclipse.emf.cdo.server/plugin.xml b/plugins/org.eclipse.emf.cdo.server/plugin.xml index f0c3af0960..f67d040a67 100644 --- a/plugins/org.eclipse.emf.cdo.server/plugin.xml +++ b/plugins/org.eclipse.emf.cdo.server/plugin.xml @@ -19,6 +19,14 @@ <extension point="org.eclipse.net4j.util.factories"> <factory + productGroup="org.eclipse.emf.cdo.server.repositoryConfigurators" + type="default" + class="org.eclipse.emf.cdo.spi.server.RepositoryConfigurator$Factory$Default"/> + <factory + productGroup="org.eclipse.emf.cdo.server.repositoryActivityLogs" + type="rolling" + class="org.eclipse.emf.cdo.spi.server.RepositoryActivityLog$Rolling$Factory"/> + <factory productGroup="org.eclipse.net4j.Negotiators" type="challenge" class="org.eclipse.net4j.util.security.ChallengeNegotiatorFactory"/> diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java index 1ffaf5358b..1a4b660bdc 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/bundle/CDOServerApplication.java @@ -36,6 +36,10 @@ public class CDOServerApplication extends OSGiApplication { public static final String ID = OM.BUNDLE_ID + ".app"; //$NON-NLS-1$ + public static final String PROP_CONFIGURATOR_TYPE = "org.eclipse.emf.cdo.server.repositoryConfiguratorType"; + + public static final String PROP_CONFIGURATOR_DESCRIPTION = "org.eclipse.emf.cdo.server.repositoryConfiguratorDescription"; + public static final String PROP_BROWSER_PORT = "org.eclipse.emf.cdo.server.browser.port"; //$NON-NLS-1$ private IRepository[] repositories; @@ -47,6 +51,13 @@ public class CDOServerApplication extends OSGiApplication super(ID); } + protected RepositoryConfigurator getConfigurator(IManagedContainer container) + { + String type = OMPlatform.INSTANCE.getProperty(PROP_CONFIGURATOR_TYPE, RepositoryConfigurator.Factory.Default.TYPE); + String description = OMPlatform.INSTANCE.getProperty(PROP_CONFIGURATOR_DESCRIPTION); + return (RepositoryConfigurator)container.getElement(RepositoryConfigurator.Factory.PRODUCT_GROUP, type, description); + } + @Override protected void doStart() throws Exception { @@ -57,7 +68,8 @@ public class CDOServerApplication extends OSGiApplication File configFile = OMPlatform.INSTANCE.getConfigFile("cdo-server.xml"); //$NON-NLS-1$ if (configFile != null && configFile.exists()) { - RepositoryConfigurator repositoryConfigurator = new RepositoryConfigurator(container); + RepositoryConfigurator repositoryConfigurator = getConfigurator(container); + repositories = repositoryConfigurator.configure(configFile); if (repositories == null || repositories.length == 0) { diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryActivityLog.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryActivityLog.java new file mode 100644 index 0000000000..16fe141762 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryActivityLog.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2004-2018 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.spi.server; + +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IRepository.WriteAccessHandler; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.container.ContainerEventAdapter; +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.factory.ProductCreationException; +import org.eclipse.net4j.util.lifecycle.LifecycleHook; +import org.eclipse.net4j.util.om.log.Log; +import org.eclipse.net4j.util.om.log.RollingLog; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Eike Stepper + * @since 4.7 + */ +public abstract class RepositoryActivityLog extends LifecycleHook<IRepository> implements Log +{ + private final IListener sessionManagerListener = new ContainerEventAdapter<ISession>() + { + @Override + protected void onAdded(IContainer<ISession> container, ISession session) + { + sessionOpened(session, concurrentSessions.incrementAndGet(), sessions.incrementAndGet()); + session.addListener(sessionListener); + } + + @Override + protected void onRemoved(IContainer<ISession> container, ISession session) + { + session.removeListener(sessionListener); + sessionClosed(session, concurrentSessions.decrementAndGet()); + } + }; + + private final IListener sessionListener = new ContainerEventAdapter<IView>() + { + @Override + protected void onAdded(IContainer<IView> container, IView view) + { + if (view instanceof ITransaction) + { + transactionOpened((ITransaction)view, concurrentTransactions.incrementAndGet(), transactions.incrementAndGet()); + } + else + { + viewOpened(view, concurrentViews.incrementAndGet(), views.incrementAndGet()); + } + } + + @Override + protected void onRemoved(IContainer<IView> container, IView view) + { + if (view instanceof ITransaction) + { + transactionClosed((ITransaction)view, concurrentTransactions.decrementAndGet()); + } + else + { + viewClosed(view, concurrentViews.decrementAndGet()); + } + } + }; + + private final WriteAccessHandler writeAccessHandler = new WriteAccessHandler() + { + public void handleTransactionBeforeCommitting(ITransaction transaction, CommitContext commitContext, OMMonitor monitor) throws RuntimeException + { + commitStarted(commitContext, concurrentCommits.incrementAndGet(), commits.incrementAndGet()); + } + + public void handleTransactionAfterCommitted(ITransaction transaction, CommitContext commitContext, OMMonitor monitor) + { + commitFinished(commitContext, concurrentCommits.decrementAndGet()); + } + }; + + private final AtomicInteger sessions = new AtomicInteger(); + + private final AtomicInteger views = new AtomicInteger(); + + private final AtomicInteger transactions = new AtomicInteger(); + + private final AtomicInteger commits = new AtomicInteger(); + + private final AtomicInteger concurrentSessions = new AtomicInteger(); + + private final AtomicInteger concurrentViews = new AtomicInteger(); + + private final AtomicInteger concurrentTransactions = new AtomicInteger(); + + private final AtomicInteger concurrentCommits = new AtomicInteger(); + + public RepositoryActivityLog() + { + } + + public IRepository getRepository() + { + return getDelegate(); + } + + public void setRepository(IRepository repository) + { + setDelegate(repository); + } + + protected void sessionOpened(ISession session, int concurrentSessions, int sessions) + { + log(formatSession(session) + " opened" + formatUser(session) + " (" + concurrentSessions + "/" + sessions + ")"); + } + + protected void sessionClosed(ISession session, int concurrentSessions) + { + log(formatSession(session) + " closed" + formatUser(session) + " (" + concurrentSessions + ")"); + } + + protected void viewOpened(IView view, int concurrentViews, int views) + { + log(formatView(view) + " opened" + formatUser(view.getSession()) + " (" + concurrentViews + "/" + views + ")"); + } + + protected void viewClosed(IView view, int concurrentViews) + { + log(formatView(view) + " closed" + formatUser(view.getSession()) + " (" + concurrentViews + ")"); + } + + protected void transactionOpened(ITransaction transaction, int concurrentTransactions, int transactions) + { + log(formatView(transaction) + " opened" + formatUser(transaction.getSession()) + " (" + concurrentTransactions + "/" + transactions + ")"); + } + + protected void transactionClosed(ITransaction transaction, int concurrentTransactions) + { + log(formatView(transaction) + " closed" + formatUser(transaction.getSession()) + " (" + concurrentTransactions + ")"); + } + + protected void commitStarted(CommitContext commitContext, int concurrentCommits, int commits) + { + ITransaction transaction = commitContext.getTransaction(); + log(formatView(transaction) + " committing " + commitContext.getBranchPoint().getTimeStamp() + formatUser(transaction.getSession()) + " (" + + concurrentCommits + "/" + commits + ")"); + } + + protected void commitFinished(CommitContext commitContext, int concurrentCommits) + { + ITransaction transaction = commitContext.getTransaction(); + log(formatView(transaction) + (commitContext.getRollbackMessage() != null ? " committed " : " rolled back ") + commitContext.getBranchPoint().getTimeStamp() + + formatUser(transaction.getSession()) + " (" + concurrentCommits + ")"); + } + + protected String formatSession(ISession session) + { + return "Session " + session.getSessionID(); + } + + protected String formatUser(ISession session) + { + String userID = session.getUserID(); + return StringUtil.isEmpty(userID) ? "" : " by user " + userID; + } + + protected String formatView(IView view) + { + return (view instanceof ITransaction ? "Transaction " : "View ") + view.getSessionID() + ":" + view.getViewID(); + } + + @Override + protected void hookDelegate(IRepository repository) + { + repository.getSessionManager().addListener(sessionManagerListener); + repository.addHandler(writeAccessHandler); + } + + @Override + protected void unhookDelegate(IRepository repository) + { + repository.removeHandler(writeAccessHandler); + repository.getSessionManager().removeListener(sessionManagerListener); + } + + /** + * @author Eike Stepper + * @since 4.7 + */ + public static abstract class Factory extends org.eclipse.net4j.util.factory.PropertiesFactory + { + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.repositoryActivityLogs"; //$NON-NLS-1$ + + public Factory(String type) + { + super(PRODUCT_GROUP, type); + } + + @Override + protected abstract RepositoryActivityLog create(Map<String, String> properties) throws ProductCreationException; + } + + /** + * @author Eike Stepper + * @since 4.7 + */ + public static class Rolling extends RepositoryActivityLog + { + private final RollingLog rollingLog; + + public Rolling(String logFile, long logSize) + { + rollingLog = new RollingLog(logFile, logSize); + } + + public void log(String line) + { + rollingLog.log(line); + } + + @Override + protected void delegateChanged(IRepository oldRepository, IRepository newRepository) + { + if (newRepository != null) + { + OM.LOG.info("Logging activities of repository " + newRepository.getName() + " to " + rollingLog.getLogFile()); + } + + } + + @Override + protected void doActivate() throws Exception + { + rollingLog.activate(); + super.doActivate(); + } + + @Override + protected void doDeactivate() throws Exception + { + super.doDeactivate(); + rollingLog.deactivate(); + } + + /** + * @author Eike Stepper + */ + public static final class Factory extends RepositoryActivityLog.Factory + { + public static final String TYPE = "rolling"; //$NON-NLS-1$ + + public Factory() + { + super(TYPE); + } + + @Override + protected RepositoryActivityLog create(Map<String, String> properties) throws ProductCreationException + { + String file = properties.get("file"); //$NON-NLS-1$ + if (file == null) + { + file = "activities"; + } + + String size = properties.get("size"); //$NON-NLS-1$ + if (StringUtil.isEmpty(size)) + { + size = "100000000"; + } + + return new RepositoryActivityLog.Rolling(file, Long.parseLong(size)); + } + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java index b9f206a472..07450bc700 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/RepositoryConfigurator.java @@ -23,6 +23,9 @@ import org.eclipse.emf.cdo.server.IStoreFactory; import org.eclipse.net4j.util.ObjectUtil; import org.eclipse.net4j.util.StringUtil; import org.eclipse.net4j.util.container.IManagedContainer; +import org.eclipse.net4j.util.container.IManagedContainer.ContainerAware; +import org.eclipse.net4j.util.factory.ProductCreationException; +import org.eclipse.net4j.util.factory.PropertiesFactory; import org.eclipse.net4j.util.om.OMPlatform; import org.eclipse.net4j.util.om.trace.ContextTracer; import org.eclipse.net4j.util.security.AuthenticatorFactory; @@ -136,6 +139,11 @@ public class RepositoryConfigurator if (container != null) { CDOServerUtil.addRepository(container, repository); + OM.LOG.info("CDO repository " + repository.getName() + " started"); + } + else + { + OM.LOG.info("CDO repository " + repository.getName() + " added"); } } @@ -207,6 +215,7 @@ public class RepositoryConfigurator setUserManager(repository, repositoryConfig); setAuthenticator(repository, repositoryConfig); + setActivityLog(repository, repositoryConfig); EPackage[] initialPackages = getInitialPackages(repositoryConfig); if (initialPackages.length != 0) @@ -339,6 +348,27 @@ public class RepositoryConfigurator } } + /** + * @since 4.7 + */ + protected void setActivityLog(InternalRepository repository, Element repositoryConfig) + { + NodeList activityLogConfig = repositoryConfig.getElementsByTagName("activityLog"); //$NON-NLS-1$ + if (activityLogConfig.getLength() > 1) + { + String repositoryName = repositoryConfig.getAttribute("name"); //$NON-NLS-1$ + throw new IllegalStateException("At most one activity log must be configured for repository " + repositoryName); //$NON-NLS-1$ + } + + if (activityLogConfig.getLength() > 0) + { + Element activityLogElement = (Element)activityLogConfig.item(0); + + RepositoryActivityLog activityLog = getContainerElement(activityLogElement, RepositoryActivityLog.Rolling.Factory.TYPE); + activityLog.setRepository(repository); + } + } + protected EPackage[] getInitialPackages(Element repositoryConfig) { List<EPackage> result = new ArrayList<EPackage>(); @@ -400,6 +430,30 @@ public class RepositoryConfigurator return storeFactory.createStore(repositoryName, repositoryProperties, storeConfig); } + /** + * @since 4.7 + */ + protected <T> T getContainerElement(Element element, String defaultType) + { + String type = element.getAttribute("type"); //$NON-NLS-1$ + if (StringUtil.isEmpty(type)) + { + type = defaultType; + } + + String description = element.getAttribute("description"); //$NON-NLS-1$ + if (StringUtil.isEmpty(description)) + { + Map<String, String> properties = getProperties(element, 1); + description = PropertiesFactory.createDescription(properties); + } + + @SuppressWarnings("unchecked") + T containerElement = (T)container.getElement(RepositoryActivityLog.Factory.PRODUCT_GROUP, type, description); + + return containerElement; + } + public static Map<String, String> getProperties(Element element, int levels) { Map<String, String> properties = new HashMap<String, String>(); @@ -454,4 +508,51 @@ public class RepositoryConfigurator return null; } + + /** + * @author Eike Stepper + * @since 4.7 + */ + public static abstract class Factory extends org.eclipse.net4j.util.factory.Factory implements ContainerAware + { + public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.repositoryConfigurators"; //$NON-NLS-1$ + + private IManagedContainer container; + + public Factory(String type) + { + super(PRODUCT_GROUP, type); + } + + public void setManagedContainer(IManagedContainer container) + { + this.container = container; + } + + public final RepositoryConfigurator create(String description) throws ProductCreationException + { + return create(container, description); + } + + public abstract RepositoryConfigurator create(IManagedContainer container, String description) throws ProductCreationException; + + /** + * @author Eike Stepper + */ + public static final class Default extends Factory + { + public static final String TYPE = "default"; //$NON-NLS-1$ + + public Default() + { + super(TYPE); + } + + @Override + public RepositoryConfigurator create(IManagedContainer container, String description) throws ProductCreationException + { + return new RepositoryConfigurator(container); + } + } + } } diff --git a/plugins/org.eclipse.net4j.util/plugin.xml b/plugins/org.eclipse.net4j.util/plugin.xml index 73d6d7c1c0..31b1dcc34e 100644 --- a/plugins/org.eclipse.net4j.util/plugin.xml +++ b/plugins/org.eclipse.net4j.util/plugin.xml @@ -42,8 +42,7 @@ <factory productGroup="org.eclipse.net4j.util.confirmationProviders" type="default" - class="org.eclipse.net4j.util.confirmation.IConfirmationProvider.Factory.Default"> - </factory> + class="org.eclipse.net4j.util.confirmation.IConfirmationProvider.Factory.Default"/> </extension> </plugin> diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/ConcurrencyUtil.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/ConcurrencyUtil.java index 0ec46b678b..7d6ef1f175 100644 --- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/ConcurrencyUtil.java +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/ConcurrencyUtil.java @@ -115,4 +115,12 @@ public final class ConcurrencyUtil thread.setDaemon(true); thread.start(); } + + /** + * @since 3.8 + */ + public static void setThreadName(Thread thread, String name) + { + thread.setName(name); + } } diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RunnableWithName.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RunnableWithName.java index 54feb65a7d..4bedf23ddd 100644 --- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RunnableWithName.java +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RunnableWithName.java @@ -44,7 +44,7 @@ public abstract class RunnableWithName implements Runnable } else { - thread.setName(name); + ConcurrencyUtil.setThreadName(thread, name); } } @@ -56,7 +56,7 @@ public abstract class RunnableWithName implements Runnable { if (thread != null) { - thread.setName(oldName); + ConcurrencyUtil.setThreadName(thread, oldName); } } } diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/factory/PropertiesFactory.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/factory/PropertiesFactory.java new file mode 100644 index 0000000000..f8e18ec6f9 --- /dev/null +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/factory/PropertiesFactory.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018 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.net4j.util.factory; + +import org.eclipse.net4j.util.StringUtil; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Eike Stepper + * @since 3.8 + */ +public abstract class PropertiesFactory extends Factory +{ + public static final String PROPERTY_SEPARATOR = "|"; + + public static final String DEFAULT_KEY = "_"; + + public PropertiesFactory(FactoryKey key) + { + super(key); + } + + public PropertiesFactory(String productGroup, String type) + { + super(productGroup, type); + } + + public Object create(String description) throws ProductCreationException + { + Map<String, String> properties = new HashMap<String, String>(); + + if (!StringUtil.isEmpty(description)) + { + String[] segments = description.split("\\" + PROPERTY_SEPARATOR); + for (String segment : segments) + { + if (!StringUtil.isEmpty(segment)) + { + int pos = segment.indexOf('='); + if (pos != -1) + { + String key = segment.substring(0, pos).trim(); + String value = segment.substring(pos + 1).trim(); + properties.put(key, value); + } + else + { + properties.put(DEFAULT_KEY, segment); + } + } + } + } + + return create(properties); + } + + protected abstract Object create(Map<String, String> properties) throws ProductCreationException; + + public static String createDescription(Map<String, String> properties) + { + StringBuilder builder = new StringBuilder(); + + String defaultValue = properties.remove(DEFAULT_KEY); + if (!StringUtil.isEmpty(defaultValue)) + { + builder.append(defaultValue); + } + + for (Map.Entry<String, String> entry : properties.entrySet()) + { + if (builder.length() != 0) + { + builder.append(PROPERTY_SEPARATOR); + } + + builder.append(entry.getKey()); + builder.append("="); + builder.append(entry.getValue()); + } + + return builder.toString(); + } +} diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/lifecycle/LifecycleHook.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/lifecycle/LifecycleHook.java new file mode 100644 index 0000000000..07a57b3d1f --- /dev/null +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/lifecycle/LifecycleHook.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2018 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.net4j.util.lifecycle; + +import org.eclipse.net4j.util.event.IEvent; +import org.eclipse.net4j.util.event.IListener; + +/** + * @author Eike Stepper + * @since 3.8 + */ +public class LifecycleHook<T extends ILifecycle> extends Lifecycle +{ + private final IListener delegateListener = new LifecycleEventAdapter() + { + @Override + protected void notifyOtherEvent(IEvent event) + { + delegateEvent(delegate, event); + } + + @Override + protected void onAboutToActivate(ILifecycle lifecycle) + { + delegateAboutToActivate(delegate); + } + + @Override + protected void onAboutToDeactivate(ILifecycle lifecycle) + { + delegateAboutToDeactivate(delegate); + } + + @Override + protected void onActivated(ILifecycle lifecycle) + { + delegateActivated(delegate); + hookDelegateIfPossible(); + } + + @Override + protected void onDeactivated(ILifecycle lifecycle) + { + unhookDelegateIfPossible(); + delegateDeactivated(delegate); + } + }; + + private T delegate; + + private boolean listening; + + private boolean delegateHooked; + + public LifecycleHook() + { + } + + protected final T getDelegate() + { + return delegate; + } + + protected final void setDelegate(T delegate) + { + T oldDelegate = this.delegate; + + if (oldDelegate != delegate) + { + unhookDelegateIfPossible(); + this.delegate = delegate; + hookDelegateIfPossible(); + + delegateChanged(oldDelegate, delegate); + } + } + + protected void delegateChanged(T oldDelegate, T newDelegate) + { + } + + protected void delegateEvent(T delegate, IEvent event) + { + } + + protected void delegateAboutToActivate(T delegate) + { + } + + protected void delegateActivated(T delegate) + { + } + + protected void delegateAboutToDeactivate(T delegate) + { + } + + protected void delegateDeactivated(T delegate) + { + } + + @Override + protected void doActivate() throws Exception + { + hookDelegateIfPossible(); + } + + @Override + protected void doDeactivate() throws Exception + { + unhookDelegateIfPossible(); + } + + protected void hookDelegate(T delegate) + { + } + + protected void unhookDelegate(T delegate) + { + } + + protected boolean hookInactiveDelegates() + { + return false; + } + + private void hookDelegateIfPossible() + { + if (!listening && delegate != null) + { + delegate.addListener(delegateListener); + listening = true; + } + + if (listening && !delegateHooked && (hookInactiveDelegates() || delegate.isActive())) + { + hookDelegate(delegate); + delegateHooked = true; + } + } + + private void unhookDelegateIfPossible() + { + if (delegate != null) + { + if (delegateHooked) + { + unhookDelegate(delegate); + delegateHooked = false; + } + + if (listening) + { + delegate.removeListener(delegateListener); + listening = false; + } + } + } +} diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/Log.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/Log.java new file mode 100644 index 0000000000..ec858c7fe6 --- /dev/null +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/Log.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018 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.net4j.util.om.log; + +/** + * @author Eike Stepper + * @since 3.8 + */ +public interface Log +{ + public void log(String line); +} diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/RollingLog.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/RollingLog.java new file mode 100644 index 0000000000..011f1e59c7 --- /dev/null +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/log/RollingLog.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2018 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.net4j.util.om.log; + +import org.eclipse.net4j.internal.util.bundle.OM; +import org.eclipse.net4j.util.collection.AbstractIterator; +import org.eclipse.net4j.util.collection.CloseableIterator; +import org.eclipse.net4j.util.concurrent.Worker; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.om.log.RollingLog.LogLine; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author Eike Stepper + * @since 3.8 + */ +public class RollingLog extends Worker implements Log, Iterable<LogLine> +{ + private final String logFile; + + private final long logSize; + + private final AtomicLong logLineCounter = new AtomicLong(0); + + private int fileNumber; + + private boolean fileAppend; + + private List<LogLine> queue = new ArrayList<LogLine>(); + + public RollingLog(String logFile, long logSize) + { + this.logFile = logFile; + this.logSize = logSize; + + setDaemon(true); + } + + public final String getLogFile() + { + return logFile; + } + + public final long getLogSize() + { + return logSize; + } + + public final void log(String line) + { + LogLine logLine = createLogLine(line); + + synchronized (this) + { + logLine.id = logLineCounter.incrementAndGet(); + queue.add(logLine); + notifyAll(); + } + } + + @Override + protected final void work(WorkContext context) throws Exception + { + List<LogLine> logLines; + synchronized (this) + { + if (queue.isEmpty()) + { + try + { + wait(100); + } + catch (InterruptedException ex) + { + context.terminate(); + } + + context.nextWork(); + } + + logLines = queue; + queue = new ArrayList<LogLine>(); + } + + writeLogLines(logLines); + } + + protected LogLine createLogLine(String line) + { + long millis = System.currentTimeMillis(); + String thread = getThreadInfo(); + + return new LogLine(millis, thread, line); + } + + protected void writeLogLines(List<LogLine> logLines) + { + if (logFile != null) + { + PrintStream out = null; + + try + { + File file; + + for (;;) + { + file = getFile(logFile, fileNumber); + + if (fileAppend && file.length() > logSize) + { + fileNumber++; + fileAppend = false; + continue; + } + + break; + } + + out = new PrintStream(new FileOutputStream(file, fileAppend)); + writeLogLines(logLines, out); + out.close(); + } + catch (IOException ex) + { + OM.LOG.error(ex); + } + finally + { + fileAppend = true; + IOUtil.closeSilent(out); + } + } + else + { + writeLogLines(logLines, System.out); + } + } + + protected void writeLogLines(List<LogLine> logLines, PrintStream out) + { + for (LogLine logLine : logLines) + { + writeLogLine(logLine, out); + } + } + + protected void writeLogLine(LogLine logLine, PrintStream out) + { + out.println(logLine); + } + + protected String getThreadInfo() + { + return Thread.currentThread().getName(); + } + + public final CloseableIterator<LogLine> iterator() + { + return iterator(logFile); + } + + public static CloseableIterator<LogLine> iterator(String logFile) + { + return new LogIterator(logFile); + } + + private static File getFile(String logFile, int fileNumber) + { + return new File(logFile + String.format("-%04d", fileNumber) + ".txt"); + } + + /** + * @author Eike Stepper + */ + private static final class LogIterator extends AbstractIterator<LogLine> implements CloseableIterator<LogLine> + { + private static final int CLOSED = -1; + + private final String logFile; + + private int fileNumber; + + private BufferedReader reader; + + public LogIterator(String logFile) + { + this.logFile = logFile; + } + + @Override + protected Object computeNextElement() + { + if (fileNumber == CLOSED) + { + return END_OF_DATA; + } + + if (reader == null) + { + File file = getFile(logFile, fileNumber++); + if (file.isFile()) + { + try + { + reader = new BufferedReader(new FileReader(file)); + } + catch (FileNotFoundException ex) + { + OM.LOG.error(ex); + return END_OF_DATA; + } + } + else + { + return END_OF_DATA; + } + } + + try + { + String string = reader.readLine(); + if (string == null) + { + reader.close(); + reader = null; + return computeNextElement(); + } + + return new LogLine(string); + } + catch (IOException ex) + { + OM.LOG.error(ex); + return END_OF_DATA; + } + } + + public void close() + { + IOUtil.close(reader); + reader = null; + fileNumber = CLOSED; + } + + public boolean isClosed() + { + return fileNumber == CLOSED; + } + } + + /** + * @author Eike Stepper + */ + public static final class LogLine + { + private static final String TAB = "\t"; + + private long id; + + private final long millis; + + private final String thread; + + private final String line; + + public LogLine(long millis, String thread, String line) + { + this.millis = millis; + this.thread = thread; + this.line = line; + } + + public LogLine(String string) + { + StringTokenizer tokenizer = new StringTokenizer(string, TAB); + id = Long.parseLong(tokenizer.nextToken()); + millis = Long.parseLong(tokenizer.nextToken()); + thread = tokenizer.nextToken(); + line = tokenizer.nextToken("").substring(1); + } + + public long getID() + { + return id; + } + + public long getMillis() + { + return millis; + } + + public String getThread() + { + return thread; + } + + public String getLine() + { + return line; + } + + @Override + public String toString() + { + return id + TAB + millis + TAB + thread + TAB + line; + } + } +} |