With the risk of sounding like a school teacher (which I'm trying to avoid)...

Browsing through some of the new tenant code, which is still a work in progress 
to be fair, I found the following piece of code that I wanted to share with you:

public class TenantServiceFactory implements ManagedServiceFactory {
    public static final String PID = "org.amdatu.core.tenant.factory";
    public static final String ID_KEY = "id";
    public static final String NAME_KEY = "name";
    public static final String HOST_KEY = "host";
    private final Map<String, Component> m_components = new HashMap<String, 
Component>();
    private volatile DependencyManager m_dependencyManager;
    private volatile LogService m_logService;

    public String getName() {
        return PID;
    }

    public synchronized void updated(String pid, Dictionary/* <String, String> 
*/properties)
        throws ConfigurationException {

This always triggers my interest, when a whole method is being synchronized, 
because that means that nothing inside this method should take a long time or 
invoke other methods, unless we can 100% guarantee that what those methods do 
is okay. If the code invokes any other service or any OSGi API including the 
dependency manager, you are almost certainly setting yourself up for hard to 
find deadlocks.

        if (m_components.containsKey(pid)) {
            m_logService.log(LogService.LOG_DEBUG, "pid already registered for 
tenant '" + properties.get(ID_KEY)
                + "', skipping registration");
            return;
        }

As a side note, this is not the way to deal with configuration updates, but I 
put that as a TODO in the documentation on Confluence for now.

        String id = (String) properties.get(ID_KEY);
        String name = (String) properties.get(NAME_KEY);
        String host = (String) properties.get(HOST_KEY);

        Properties serviceProperties = new Properties();
        serviceProperties.put(Tenant.TENANT_ID_SERVICEPROPERTY, id);
        serviceProperties.put(Tenant.TENANT_NAME_SERVICEPROPERTY, name);
        serviceProperties.put(Tenant.TENANT_SERVICEPROPERTY + "hostname", host);

        Tenant tenant = new TenantImpl(id, name);
        tenant.putProperty("hostname", host);

        Component component = m_dependencyManager.createComponent()
            .setInterface(Tenant.class.getName(), serviceProperties)
            .setImplementation(tenant);

Up to here we're fine. We are invoking the dependency manager, but only 
declaring a component without actually handing it to the manager is fine.

        m_dependencyManager.add(component);

This most certainly won't work, as adding a component to the manager can 
generate a call chain that, via all kinds of services that you have no clue 
about, might trigger a call to your own service. With the right mix of 
multithreading and locking this will at some point go wrong. This must be 
invoked without holding any locks.

        m_components.put(pid, component);
    }

    public synchronized void deleted(String pid) {

The same applies here, you cannot call the log service while holding a lock. 
Besides that, the framework already logs and sends events when services go 
away, so there is little point in actually logging this ourselves here.

        m_logService.log(LogService.LOG_INFO, "Unregistering tenant service 
with pid: '" + pid + "'");

For removing things from the dependency manager, the same applies as adding 
things in the sense that you just don't know what will be called because of 
this, so you cannot hold the lock.

        m_dependencyManager.remove(m_components.remove(pid));
    }
}

Greetings, Marcel

_______________________________________________
Amdatu-developers mailing list
[email protected]
http://lists.amdatu.org/mailman/listinfo/amdatu-developers

Reply via email to