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