001/*
002 * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Florent Guillaume
016 */
017
018package org.nuxeo.runtime.datasource;
019
020import java.util.HashMap;
021import java.util.Map;
022
023import javax.naming.CompositeName;
024import javax.naming.Context;
025import javax.naming.Name;
026import javax.naming.NamingException;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.nuxeo.runtime.jtajca.NuxeoContainer;
031import org.nuxeo.runtime.model.ComponentContext;
032import org.nuxeo.runtime.model.ComponentInstance;
033import org.nuxeo.runtime.model.DefaultComponent;
034
035/**
036 * Nuxeo component allowing the JNDI registration of datasources by extension point contributions.
037 * <p>
038 * For now only the internal Nuxeo JNDI server is supported.
039 */
040public class DataSourceComponent extends DefaultComponent {
041
042    private final Log log = LogFactory.getLog(DataSourceComponent.class);
043
044    public static final String DATASOURCES_XP = "datasources";
045
046    public static final String ENV_CTX_NAME = "java:comp/env/";
047
048    protected final Map<String, DataSourceDescriptor> datasources = new HashMap<String, DataSourceDescriptor>();
049
050    protected final Map<String, DataSourceLinkDescriptor> links = new HashMap<String, DataSourceLinkDescriptor>();
051
052    protected final PooledDataSourceRegistry registry = new PooledDataSourceRegistry();
053
054    protected Context namingContext;
055
056    @Override
057    public void registerContribution(Object contrib, String extensionPoint, ComponentInstance component) {
058        if (DATASOURCES_XP.equals(extensionPoint)) {
059            if (contrib instanceof DataSourceDescriptor) {
060                addDataSource((DataSourceDescriptor) contrib);
061            } else if (contrib instanceof DataSourceLinkDescriptor) {
062                addDataSourceLink((DataSourceLinkDescriptor) contrib);
063            } else {
064                log.error("Wrong datasource extension type " + contrib.getClass().getName());
065            }
066        } else {
067            log.error("Ignoring unknown extension point: " + extensionPoint);
068        }
069    }
070
071    @Override
072    public void unregisterContribution(Object contrib, String extensionPoint, ComponentInstance component) {
073        if (DATASOURCES_XP.equals(extensionPoint)) {
074            if (contrib instanceof DataSourceDescriptor) {
075                removeDataSource((DataSourceDescriptor) contrib);
076            } else if (contrib instanceof DataSourceLinkDescriptor) {
077                removeDataSourceLink((DataSourceLinkDescriptor) contrib);
078            }
079        }
080    }
081
082    @Override
083    public int getApplicationStartedOrder() {
084        return -1000;
085    }
086
087    public boolean isStarted() {
088        return namingContext != null;
089    }
090
091    @Override
092    public void applicationStarted(ComponentContext context) {
093        if (namingContext != null) {
094            return;
095        }
096        namingContext = NuxeoContainer.getRootContext();
097        // allocate datasource sub-contexts
098        Name comp;
099        try {
100            comp = new CompositeName(DataSourceHelper.getDataSourceJNDIPrefix());
101        } catch (NamingException e) {
102            throw new RuntimeException(e);
103        }
104        Context ctx = namingContext;
105        for (int i = 0; i < comp.size(); i++) {
106            try {
107                ctx = (Context) ctx.lookup(comp.get(i));
108            } catch (NamingException e) {
109                try {
110                    ctx = ctx.createSubcontext(comp.get(i));
111                } catch (NamingException e1) {
112                    throw new RuntimeException(e1);
113                }
114            }
115        }
116        // bind datasources
117        for (DataSourceDescriptor datasourceDesc : datasources.values()) {
118            bindDataSource(datasourceDesc);
119        }
120        // bind links
121        for (DataSourceLinkDescriptor linkDesc : links.values()) {
122            bindDataSourceLink(linkDesc);
123        }
124    }
125
126    @Override
127    public void deactivate(ComponentContext context) {
128        super.deactivate(context);
129        for (DataSourceLinkDescriptor desc : links.values()) {
130            unbindDataSourceLink(desc);
131        }
132        links.clear();
133        for (DataSourceDescriptor desc : datasources.values()) {
134            unbindDataSource(desc);
135        }
136        datasources.clear();
137        namingContext = null;
138    }
139
140    protected void addDataSource(DataSourceDescriptor contrib) {
141        datasources.put(contrib.getName(), contrib);
142        bindDataSource(contrib);
143    }
144
145    protected void removeDataSource(DataSourceDescriptor contrib) {
146        unbindDataSource(contrib);
147        datasources.remove(contrib.getName());
148    }
149
150    protected void bindDataSource(DataSourceDescriptor descr) {
151        if (namingContext == null) {
152            return;
153        }
154        log.info("Registering datasource: " + descr.getName());
155        try {
156            descr.bindSelf(namingContext);
157        } catch (NamingException e) {
158            log.error("Cannot bind datasource '" + descr.getName() + "' in JNDI", e);
159        }
160    }
161
162    protected void unbindDataSource(DataSourceDescriptor descr) {
163        if (namingContext == null) {
164            return;
165        }
166        log.info("Unregistering datasource: " + descr.name);
167        try {
168            descr.unbindSelf(namingContext);
169        } catch (NamingException cause) {
170            log.error("Cannot unbind datasource '" + descr.name + "' in JNDI", cause);
171        }
172    }
173
174    protected void addDataSourceLink(DataSourceLinkDescriptor contrib) {
175        links.put(contrib.name, contrib);
176        bindDataSourceLink(contrib);
177    }
178
179    protected void removeDataSourceLink(DataSourceLinkDescriptor contrib) {
180        unbindDataSourceLink(contrib);
181        links.remove(contrib.name);
182    }
183
184    protected void bindDataSourceLink(DataSourceLinkDescriptor descr) {
185        if (namingContext == null) {
186            return;
187        }
188        log.info("Registering DataSourceLink: " + descr.name);
189        try {
190            descr.bindSelf(namingContext);
191        } catch (NamingException e) {
192            log.error("Cannot bind DataSourceLink '" + descr.name + "' in JNDI", e);
193        }
194    }
195
196    protected void unbindDataSourceLink(DataSourceLinkDescriptor descr) {
197        if (namingContext == null) {
198            return;
199        }
200        log.info("Unregistering DataSourceLink: " + descr.name);
201        try {
202            descr.unbindSelf(namingContext);
203        } catch (NamingException e) {
204            log.error("Cannot unbind DataSourceLink '" + descr.name + "' in JNDI", e);
205        }
206    }
207
208    @Override
209    public <T> T getAdapter(Class<T> adapter) {
210        if (adapter.isAssignableFrom(PooledDataSourceRegistry.class)) {
211            return adapter.cast(registry);
212        }
213        return super.getAdapter(adapter);
214    }
215
216}