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