blob: 370a59f578d8079fd7fb7d8a2b004113e8ebd33a [file] [log] [blame]
/*
* Copyright (c) 2008, 2009, 2011-2013, 2016, 2018 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) - bug 399641: container-aware factories
*/
package org.eclipse.net4j.util.container;
import org.eclipse.net4j.internal.util.bundle.OM;
import org.eclipse.net4j.util.ObjectUtil;
import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
import org.eclipse.net4j.util.event.EventUtil;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.factory.FactoryKey;
import org.eclipse.net4j.util.factory.IFactory;
import org.eclipse.net4j.util.factory.IFactoryKey;
import org.eclipse.net4j.util.factory.ProductCreationException;
import org.eclipse.net4j.util.lifecycle.ILifecycle;
import org.eclipse.net4j.util.lifecycle.Lifecycle;
import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter;
import org.eclipse.net4j.util.lifecycle.LifecycleException;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.net4j.util.registry.HashMapRegistry;
import org.eclipse.net4j.util.registry.IRegistry;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* A default implementation of a {@link IManagedContainer managed container}.
*
* @author Eike Stepper
*/
public class ManagedContainer extends Lifecycle implements IManagedContainer
{
private String name;
private IRegistry<IFactoryKey, IFactory> factoryRegistry;
private List<IElementProcessor> postProcessors;
private IRegistry<ElementKey, Object> elementRegistry = new HashMapRegistry<ElementKey, Object>();
@ExcludeFromDump
private transient long maxElementID;
@ExcludeFromDump
private transient IListener elementListener = new LifecycleEventAdapter()
{
@Override
protected void onDeactivated(ILifecycle lifecycle)
{
for (Entry<ElementKey, Object> entry : getElementRegistryEntries())
{
Object value = entry.getValue();
if (lifecycle == value)
{
ElementKey key = entry.getKey();
removeElement(key);
return;
}
}
}
};
public ManagedContainer()
{
}
/**
* @since 3.8
*/
public String getName()
{
return name;
}
/**
* @since 3.8
*/
public void setName(String name)
{
checkInactive();
this.name = name;
}
public synchronized IRegistry<IFactoryKey, IFactory> getFactoryRegistry()
{
if (factoryRegistry == null)
{
factoryRegistry = createFactoryRegistry();
factoryRegistry.addListener(new ContainerEventAdapter<Map.Entry<IFactoryKey, IFactory>>()
{
@Override
protected void onAdded(IContainer<Map.Entry<IFactoryKey, IFactory>> container, Map.Entry<IFactoryKey, IFactory> entry)
{
updateFactory(entry, ManagedContainer.this);
}
@Override
protected void onRemoved(IContainer<Map.Entry<IFactoryKey, IFactory>> container, Map.Entry<IFactoryKey, IFactory> entry)
{
updateFactory(entry, null);
}
private void updateFactory(Map.Entry<IFactoryKey, IFactory> entry, IManagedContainer container)
{
IFactory factory = entry.getValue();
if (factory instanceof ContainerAware)
{
ContainerAware f = (ContainerAware)factory;
f.setManagedContainer(container);
}
}
});
}
return factoryRegistry;
}
public ManagedContainer registerFactory(IFactory factory)
{
getFactoryRegistry().put(factory.getKey(), factory);
return this;
}
public synchronized List<IElementProcessor> getPostProcessors()
{
if (postProcessors == null)
{
postProcessors = createPostProcessors();
}
return postProcessors;
}
public synchronized void addPostProcessor(IElementProcessor postProcessor, boolean processExistingElements)
{
if (processExistingElements)
{
ContainerEvent<Object> event = new ContainerEvent<Object>(this);
for (Entry<ElementKey, Object> entry : getElementRegistryEntries())
{
ElementKey key = entry.getKey();
Object element = entry.getValue();
String productGroup = key.getProductGroup();
String factoryType = key.getFactoryType();
String description = key.getDescription();
Object newElement = postProcessor.process(this, productGroup, factoryType, description, element);
if (newElement != element)
{
synchronized (elementRegistry)
{
elementRegistry.put(key, newElement);
}
event.addDelta(element, IContainerDelta.Kind.REMOVED);
event.addDelta(newElement, IContainerDelta.Kind.ADDED);
}
}
fireEvent(event);
}
getPostProcessors().add(postProcessor);
}
public void addPostProcessor(IElementProcessor postProcessor)
{
addPostProcessor(postProcessor, false);
}
public void removePostProcessor(IElementProcessor postProcessor)
{
getPostProcessors().remove(postProcessor);
}
public Set<String> getProductGroups()
{
checkActive();
Set<String> result = new HashSet<String>();
for (IFactoryKey key : factoryRegistry.keySet())
{
result.add(key.getProductGroup());
}
for (ElementKey key : getElementRegistryKeys())
{
result.add(key.getProductGroup());
}
return result;
}
public Set<String> getFactoryTypes(String productGroup)
{
checkActive();
Set<String> result = new HashSet<String>();
for (IFactoryKey key : factoryRegistry.keySet())
{
if (ObjectUtil.equals(key.getProductGroup(), productGroup))
{
result.add(key.getType());
}
}
for (ElementKey key : getElementRegistryKeys())
{
if (ObjectUtil.equals(key.getProductGroup(), productGroup))
{
result.add(key.getFactoryType());
}
}
return result;
}
public IFactory getFactory(String productGroup, String factoryType) throws FactoryNotFoundException
{
FactoryKey key = new FactoryKey(productGroup, factoryType);
IFactory factory = getFactoryRegistry().get(key);
if (factory == null)
{
throw new FactoryNotFoundException("Factory not found: " + key); //$NON-NLS-1$
}
return factory;
}
public boolean isEmpty()
{
checkActive();
synchronized (elementRegistry)
{
return elementRegistry.isEmpty();
}
}
public String[] getElementKey(Object element)
{
checkActive();
for (Entry<ElementKey, Object> entry : getElementRegistryEntries())
{
if (entry.getValue() == element)
{
ElementKey key = entry.getKey();
String[] result = { key.getProductGroup(), key.getFactoryType(), key.getDescription() };
return result;
}
}
return null;
}
public Object[] getElements()
{
checkActive();
return getElementRegistryValues();
}
public Object[] getElements(String productGroup)
{
checkActive();
List<Object> result = new ArrayList<Object>();
for (Entry<ElementKey, Object> entry : getElementRegistryEntries())
{
ElementKey key = entry.getKey();
if (ObjectUtil.equals(key.getProductGroup(), productGroup))
{
result.add(entry.getValue());
}
}
return result.toArray();
}
public Object[] getElements(String productGroup, String factoryType)
{
checkActive();
List<Object> result = new ArrayList<Object>();
for (Entry<ElementKey, Object> entry : getElementRegistryEntries())
{
ElementKey key = entry.getKey();
if (ObjectUtil.equals(key.getProductGroup(), productGroup) && ObjectUtil.equals(key.getFactoryType(), factoryType))
{
result.add(entry.getValue());
}
}
return result.toArray();
}
public Object getElement(String productGroup, String factoryType, String description) throws FactoryNotFoundException, ProductCreationException
{
return getElement(productGroup, factoryType, description, true);
}
/**
* @since 2.0
*/
public Object getElement(String productGroup, String factoryType, String description, boolean activate)
throws FactoryNotFoundException, ProductCreationException
{
checkActive();
ElementKey key = new ElementKey(productGroup, factoryType, description);
Object element;
synchronized (elementRegistry)
{
element = elementRegistry.get(key);
}
if (element == null)
{
element = createElement(productGroup, factoryType, description);
element = postProcessElement(productGroup, factoryType, description, element);
if (activate)
{
activateElement(element);
}
putElement(key, element);
}
return element;
}
/**
* @since 3.2
*/
protected void activateElement(Object element)
{
EventUtil.addUniqueListener(element, elementListener);
LifecycleUtil.activate(element);
boolean active;
if (LifecycleUtil.isDeferredActivation(element))
{
active = LifecycleUtil.waitForActive(element, 10 * 1000);
}
else
{
active = LifecycleUtil.isActive(element);
}
if (!active)
{
EventUtil.removeListener(element, elementListener);
throw new LifecycleException("Could not activate " + element);
}
}
public Object putElement(String productGroup, String factoryType, String description, Object element)
{
checkActive();
element = postProcessElement(productGroup, factoryType, description, element);
ElementKey key = new ElementKey(productGroup, factoryType, description);
return putElement(key, element);
}
protected Object putElement(ElementKey key, Object element)
{
ContainerEvent<Object> event = new ContainerEvent<Object>(this);
Object oldElement;
synchronized (elementRegistry)
{
key.setID(++maxElementID);
oldElement = elementRegistry.put(key, element);
}
if (oldElement != element)
{
if (element instanceof ContainerAware)
{
((ContainerAware)element).setManagedContainer(this);
}
EventUtil.addUniqueListener(element, elementListener);
if (oldElement != null)
{
EventUtil.removeListener(oldElement, elementListener);
event.addDelta(oldElement, IContainerDelta.Kind.REMOVED);
}
event.addDelta(element, IContainerDelta.Kind.ADDED);
fireEvent(event);
}
return oldElement;
}
public Object removeElement(String productGroup, String factoryType, String description)
{
checkActive();
ElementKey key = new ElementKey(productGroup, factoryType, description);
return removeElement(key);
}
protected Object removeElement(ElementKey key)
{
Object element;
synchronized (elementRegistry)
{
element = elementRegistry.remove(key);
}
if (element != null)
{
EventUtil.removeListener(element, elementListener);
fireEvent(new SingleDeltaContainerEvent<Object>(this, element, IContainerDelta.Kind.REMOVED));
if (element instanceof ContainerAware)
{
((ContainerAware)element).setManagedContainer(null);
}
}
return element;
}
public void clearElements()
{
checkActive();
ContainerEvent<Object> event = null;
synchronized (elementRegistry)
{
if (!elementRegistry.isEmpty())
{
event = new ContainerEvent<Object>(this);
for (Object element : elementRegistry.values())
{
EventUtil.removeListener(element, elementListener);
event.addDelta(element, IContainerDelta.Kind.REMOVED);
}
elementRegistry.clear();
}
}
if (event != null)
{
fireEvent(event);
}
}
public void loadElements(InputStream stream) throws IOException, FactoryNotFoundException, ProductCreationException
{
checkActive();
synchronized (elementRegistry)
{
clearElements();
ObjectInputStream ois = new ObjectInputStream(stream);
int size = ois.readInt();
for (int i = 0; i < size; i++)
{
try
{
ElementKey key = (ElementKey)ois.readObject();
Object element = getElement(key.getProductGroup(), key.getFactoryType(), key.getDescription());
boolean active = ois.readBoolean();
if (active)
{
// TODO Reconsider activation
LifecycleUtil.activate(element);
}
}
catch (ClassNotFoundException cannotHappen)
{
}
}
initMaxElementID();
}
}
public void saveElements(OutputStream stream) throws IOException
{
checkActive();
synchronized (elementRegistry)
{
ObjectOutputStream oos = new ObjectOutputStream(stream);
List<Entry<ElementKey, Object>> entries = new ArrayList<Entry<ElementKey, Object>>(elementRegistry.entrySet());
Collections.sort(entries, new EntryComparator());
oos.writeInt(entries.size());
for (Entry<ElementKey, Object> entry : entries)
{
oos.writeObject(entry.getKey());
oos.writeBoolean(LifecycleUtil.isActive(entry.getValue()));
}
}
}
@Override
public void fireEvent(IEvent event)
{
if (event instanceof IContainerEvent<?>)
{
@SuppressWarnings("unchecked")
IContainerEvent<Object> e = (IContainerEvent<Object>)event;
if (e.isEmpty())
{
return;
}
}
super.fireEvent(event);
}
@Override
public String toString()
{
if (name != null)
{
return getTypeName() + "[" + name + "]";
}
return getTypeName();
}
/**
* @since 3.8
*/
protected String getTypeName()
{
return "ManagedContainer"; //$NON-NLS-1$
}
protected IRegistry<IFactoryKey, IFactory> createFactoryRegistry()
{
return new HashMapRegistry<IFactoryKey, IFactory>();
}
protected List<IElementProcessor> createPostProcessors()
{
return new ArrayList<IElementProcessor>();
}
/**
* @since 2.0
*/
protected ElementKey[] getElementRegistryKeys()
{
synchronized (elementRegistry)
{
return elementRegistry.keySet().toArray(new ElementKey[elementRegistry.size()]);
}
}
/**
* @since 2.0
*/
protected Object[] getElementRegistryValues()
{
synchronized (elementRegistry)
{
return elementRegistry.values().toArray(new Object[elementRegistry.size()]);
}
}
/**
* @since 2.0
*/
@SuppressWarnings("unchecked")
protected Entry<ElementKey, Object>[] getElementRegistryEntries()
{
synchronized (elementRegistry)
{
return elementRegistry.entrySet().toArray(new Entry[elementRegistry.size()]);
}
}
protected Object createElement(String productGroup, String factoryType, String description) throws FactoryNotFoundException, ProductCreationException
{
IFactory factory = getFactory(productGroup, factoryType);
return factory.create(description);
}
protected Object postProcessElement(String productGroup, String factoryType, String description, Object element)
{
for (IElementProcessor processor : getPostProcessors())
{
element = processor.process(this, productGroup, factoryType, description, element);
}
return element;
}
private void initMaxElementID()
{
maxElementID = 0L;
for (ElementKey key : elementRegistry.keySet())
{
long id = key.getID();
if (maxElementID < id)
{
maxElementID = id;
}
}
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
LifecycleUtil.activate(getFactoryRegistry());
LifecycleUtil.activate(getPostProcessors());
}
@Override
protected void doDeactivate() throws Exception
{
for (Object element : getElementRegistryValues())
{
try
{
LifecycleUtil.deactivateNoisy(element);
EventUtil.removeListener(element, elementListener);
}
catch (RuntimeException ex)
{
OM.LOG.warn(ex);
}
}
LifecycleUtil.deactivate(factoryRegistry);
LifecycleUtil.deactivate(postProcessors);
elementRegistry.clear();
super.doDeactivate();
}
/**
* @author Eike Stepper
*/
private static final class ElementKey implements Serializable, Comparable<ElementKey>
{
private static final long serialVersionUID = 1L;
private long id;
private String productGroup;
private String factoryType;
private String description;
public ElementKey(String productGroup, String factoryType, String description)
{
this.productGroup = productGroup;
this.factoryType = factoryType;
this.description = description;
}
public long getID()
{
return id;
}
public void setID(long id)
{
this.id = id;
}
public String getProductGroup()
{
return productGroup;
}
public String getFactoryType()
{
return factoryType;
}
public String getDescription()
{
return description;
}
@Override
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (obj instanceof ElementKey)
{
ElementKey key = (ElementKey)obj;
return ObjectUtil.equals(productGroup, key.productGroup) && ObjectUtil.equals(factoryType, key.factoryType)
&& ObjectUtil.equals(description, key.description);
}
return false;
}
@Override
public int hashCode()
{
return ObjectUtil.hashCode(productGroup) ^ ObjectUtil.hashCode(factoryType) ^ ObjectUtil.hashCode(description);
}
@Override
public String toString()
{
return MessageFormat.format("{0}[{1}, {2}]", productGroup, factoryType, description); //$NON-NLS-1$
}
public int compareTo(ElementKey key)
{
if (id < key.id)
{
return -1;
}
if (id > key.id)
{
return 1;
}
return 0;
}
}
/**
* @author Eike Stepper
*/
private static final class EntryComparator implements Comparator<Entry<ElementKey, Object>>
{
public int compare(Entry<ElementKey, Object> entry1, Entry<ElementKey, Object> entry2)
{
return entry1.getKey().compareTo(entry2.getKey());
}
}
}