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.datasource.DatasourceExceptionSorter.Configuration;
033import org.nuxeo.runtime.jtajca.NuxeoContainer;
034import org.nuxeo.runtime.model.ComponentContext;
035import org.nuxeo.runtime.model.ComponentInstance;
036import org.nuxeo.runtime.model.DefaultComponent;
037
038/**
039 * Nuxeo component allowing the JNDI registration of datasources by extension
040 * point contributions.
041 * <p>
042 * For now only the internal Nuxeo JNDI server is supported.
043 */
044public class DataSourceComponent extends DefaultComponent {
045
046    private final Log log = LogFactory.getLog(DataSourceComponent.class);
047
048    static DataSourceComponent instance;
049
050    public static final String DATASOURCES_XP = "datasources";
051
052    public static final String ENV_CTX_NAME = "java:comp/env/";
053
054    protected final Map<String, DataSourceDescriptor> datasources = new HashMap<>();
055
056    protected final Map<String, DataSourceLinkDescriptor> links = new HashMap<>();
057
058    protected final DatasourceExceptionSorter.Registry sorterRegistry = new DatasourceExceptionSorter.Registry();
059
060    protected final PooledDataSourceRegistry poolRegistry = new PooledDataSourceRegistry();
061
062    protected Context namingContext;
063
064    @Override
065    public void activate(ComponentContext context) {
066        instance = this;
067    }
068
069    public void deactivate() {
070        instance = null;
071    }
072
073    @Override
074    public void registerContribution(Object contrib, String extensionPoint, ComponentInstance component) {
075        if (contrib instanceof DataSourceDescriptor) {
076            addDataSource((DataSourceDescriptor) contrib);
077        } else if (contrib instanceof DataSourceLinkDescriptor) {
078            addDataSourceLink((DataSourceLinkDescriptor) contrib);
079        } else if (contrib instanceof DatasourceExceptionSorter.Configuration) {
080            sorterRegistry.addContribution((Configuration) contrib);
081        } else {
082            log.error("Wrong datasource extension type " + contrib.getClass().getName());
083        }
084    }
085
086    @Override
087    public void unregisterContribution(Object contrib, String extensionPoint, ComponentInstance component) {
088        if (contrib instanceof DataSourceDescriptor) {
089            removeDataSource((DataSourceDescriptor) contrib);
090        } else if (contrib instanceof DataSourceLinkDescriptor) {
091            removeDataSourceLink((DataSourceLinkDescriptor) contrib);
092        } else if (contrib instanceof DatasourceExceptionSorter.Configuration) {
093            sorterRegistry.removeContribution((Configuration) contrib);
094        }
095    }
096
097    @Override
098    public int getApplicationStartedOrder() {
099        return -1000;
100    }
101
102    public boolean isStarted() {
103        return namingContext != null;
104    }
105
106    @Override
107    public void applicationStarted(ComponentContext context) {
108        if (namingContext != null) {
109            return;
110        }
111        namingContext = NuxeoContainer.getRootContext();
112        // allocate datasource sub-contexts
113        Name comp;
114        try {
115            comp = new CompositeName(DataSourceHelper.getDataSourceJNDIPrefix());
116        } catch (NamingException e) {
117            throw new RuntimeException(e);
118        }
119        Context ctx = namingContext;
120        for (int i = 0; i < comp.size(); i++) {
121            try {
122                ctx = (Context) ctx.lookup(comp.get(i));
123            } catch (NamingException e) {
124                try {
125                    ctx = ctx.createSubcontext(comp.get(i));
126                } catch (NamingException e1) {
127                    throw new RuntimeException(e1);
128                }
129            }
130        }
131        // bind datasources
132        for (DataSourceDescriptor datasourceDesc : datasources.values()) {
133            bindDataSource(datasourceDesc);
134        }
135        // bind links
136        for (DataSourceLinkDescriptor linkDesc : links.values()) {
137            bindDataSourceLink(linkDesc);
138        }
139    }
140
141    @Override
142    public void deactivate(ComponentContext context) {
143        super.deactivate(context);
144        for (DataSourceLinkDescriptor desc : links.values()) {
145            unbindDataSourceLink(desc);
146        }
147        links.clear();
148        for (DataSourceDescriptor desc : datasources.values()) {
149            unbindDataSource(desc);
150        }
151        datasources.clear();
152        namingContext = null;
153    }
154
155    protected void addDataSource(DataSourceDescriptor contrib) {
156        datasources.put(contrib.getName(), contrib);
157        bindDataSource(contrib);
158    }
159
160    protected void removeDataSource(DataSourceDescriptor contrib) {
161        unbindDataSource(contrib);
162        datasources.remove(contrib.getName());
163    }
164
165    protected void bindDataSource(DataSourceDescriptor descr) {
166        if (namingContext == null) {
167            return;
168        }
169        log.info("Registering datasource: " + descr.getName());
170        try {
171            descr.bindSelf(namingContext);
172        } catch (NamingException e) {
173            log.error("Cannot bind datasource '" + descr.getName() + "' in JNDI", e);
174        }
175    }
176
177    protected void unbindDataSource(DataSourceDescriptor descr) {
178        if (namingContext == null) {
179            return;
180        }
181        log.info("Unregistering datasource: " + descr.name);
182        try {
183            descr.unbindSelf(namingContext);
184        } catch (NamingException cause) {
185            log.error("Cannot unbind datasource '" + descr.name + "' in JNDI", cause);
186        }
187    }
188
189    protected void addDataSourceLink(DataSourceLinkDescriptor contrib) {
190        links.put(contrib.name, contrib);
191        bindDataSourceLink(contrib);
192    }
193
194    protected void removeDataSourceLink(DataSourceLinkDescriptor contrib) {
195        unbindDataSourceLink(contrib);
196        links.remove(contrib.name);
197    }
198
199    protected void bindDataSourceLink(DataSourceLinkDescriptor descr) {
200        if (namingContext == null) {
201            return;
202        }
203        log.info("Registering DataSourceLink: " + descr.name);
204        try {
205            descr.bindSelf(namingContext);
206        } catch (NamingException e) {
207            log.error("Cannot bind DataSourceLink '" + descr.name + "' in JNDI", e);
208        }
209    }
210
211    protected void unbindDataSourceLink(DataSourceLinkDescriptor descr) {
212        if (namingContext == null) {
213            return;
214        }
215        log.info("Unregistering DataSourceLink: " + descr.name);
216        try {
217            descr.unbindSelf(namingContext);
218        } catch (NamingException e) {
219            log.error("Cannot unbind DataSourceLink '" + descr.name + "' in JNDI", e);
220        }
221    }
222
223    @Override
224    public <T> T getAdapter(Class<T> adapter) {
225        if (adapter.isAssignableFrom(PooledDataSourceRegistry.class)) {
226            return adapter.cast(poolRegistry);
227        }
228        return super.getAdapter(adapter);
229    }
230
231}