001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 */
016package org.kuali.rice.krad.config;
017
018import org.kuali.rice.core.api.config.module.RunMode;
019import org.kuali.rice.core.api.config.property.ConfigContext;
020import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
021import org.kuali.rice.core.framework.config.module.ModuleConfigurer;
022import org.kuali.rice.krad.service.DataDictionaryService;
023import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
024import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
025import org.kuali.rice.krad.util.KRADConstants;
026import org.springframework.context.ApplicationEvent;
027import org.springframework.context.ApplicationListener;
028import org.springframework.context.event.ContextRefreshedEvent;
029import org.springframework.context.event.SmartApplicationListener;
030
031import javax.sql.DataSource;
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.List;
035import java.util.concurrent.Executors;
036import java.util.concurrent.ScheduledExecutorService;
037import java.util.concurrent.TimeUnit;
038
039public class KRADConfigurer extends ModuleConfigurer implements SmartApplicationListener {
040
041    private DataSource applicationDataSource;
042
043    private boolean includeKnsSpringBeans;
044
045    private static final String KRAD_SPRING_BEANS_PATH = "classpath:org/kuali/rice/krad/config/KRADSpringBeans.xml";
046    private static final String KNS_SPRING_BEANS_PATH = "classpath:org/kuali/rice/kns/config/KNSSpringBeans.xml";
047
048    public KRADConfigurer() {
049        // TODO really the constant value should be "krad" but there's some work to do in order to make
050        // that really work, see KULRICE-6532
051        super(KRADConstants.KR_MODULE_NAME);
052        setValidRunModes(Arrays.asList(RunMode.LOCAL));
053        setIncludeKnsSpringBeans(true);
054    }
055
056    @Override
057    public void addAdditonalToConfig() {
058        configureDataSource();
059    }
060
061    @Override
062    public List<String> getPrimarySpringFiles() {
063        final List<String> springFileLocations = new ArrayList<String>();
064        springFileLocations.add(KRAD_SPRING_BEANS_PATH);
065
066        if (isIncludeKnsSpringBeans()) {
067            springFileLocations.add(KNS_SPRING_BEANS_PATH);
068        }
069
070        return springFileLocations;
071    }
072
073    @Override
074    public void onApplicationEvent(ApplicationEvent applicationEvent) {
075        if (applicationEvent instanceof ContextRefreshedEvent) {
076            loadDataDictionary();
077            publishDataDictionaryComponents();
078        }
079    }
080
081    @Override
082    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
083        return true;
084    }
085
086    @Override
087    public boolean supportsSourceType(Class<?> aClass) {
088        return true;
089    }
090
091    @Override
092    public int getOrder() {
093        // return a lower value which will give the data dictionary indexing higher precedence since DD indexing should
094        // be started as soon as it can be
095        return -1000;
096    }
097
098    /**
099     * Used to "poke" the Data Dictionary again after the Spring Context is initialized.  This is to
100     * allow for modules loaded with KualiModule after the KNS has already been initialized to work.
101     *
102     * Also initializes the DateTimeService
103     */
104    protected void loadDataDictionary() {
105        if (isLoadDataDictionary()) {
106            LOG.info("KRAD Configurer - Loading DD");
107            DataDictionaryService dds = KRADServiceLocatorWeb.getDataDictionaryService();
108            if (dds == null) {
109                dds = (DataDictionaryService) GlobalResourceLoader
110                        .getService(KRADServiceLocatorWeb.DATA_DICTIONARY_SERVICE);
111            }
112            dds.getDataDictionary().parseDataDictionaryConfigurationFiles(false);
113
114            if (isValidateDataDictionary()) {
115                LOG.info("KRAD Configurer - Validating DD");
116                dds.getDataDictionary().validateDD(isValidateDataDictionaryEboReferences());
117            }
118
119            // KULRICE-4513 After the Data Dictionary is loaded and validated, perform Data Dictionary bean overrides.
120            dds.getDataDictionary().performBeanOverrides();
121        }
122    }
123
124    protected void publishDataDictionaryComponents() {
125        if (isComponentPublishingEnabled()) {
126            long delay = getComponentPublishingDelay();
127            LOG.info("Publishing of Data Dictionary components is enabled, scheduling publish after " + delay + " millisecond delay");
128            ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
129            try {
130                scheduler.schedule(new Runnable() {
131                    @Override
132                    public void run() {
133                        long start = System.currentTimeMillis();
134                        LOG.info("Executing scheduled Data Dictionary component publishing...");
135                        try {
136                            KRADServiceLocatorInternal.getDataDictionaryComponentPublisherService().publishAllComponents();
137                        } catch (RuntimeException e) {
138                            LOG.error("Failed to publish data dictionary components.", e);
139                            throw e;
140                        } finally {
141                            long end = System.currentTimeMillis();
142                            LOG.info("... finished scheduled execution of Data Dictionary component publishing.  Took " + (end-start) + " milliseconds");
143                        }
144                    }
145                }, delay, TimeUnit.MILLISECONDS);
146            } finally {
147                scheduler.shutdown();
148            }
149        }
150    }
151
152    @Override
153    public boolean hasWebInterface() {
154        return true;
155    }
156
157    /**
158     * Returns true - KNS UI should always be included.
159     *
160     * @see org.kuali.rice.core.framework.config.module.ModuleConfigurer#shouldRenderWebInterface()
161     */
162    @Override
163    public boolean shouldRenderWebInterface() {
164        return true;
165    }
166
167
168
169    public boolean isLoadDataDictionary() {
170        return ConfigContext.getCurrentContextConfig().getBooleanProperty("load.data.dictionary", true);
171    }
172
173    public boolean isValidateDataDictionary() {
174        return ConfigContext.getCurrentContextConfig().getBooleanProperty("validate.data.dictionary", false);
175    }
176
177    public boolean isValidateDataDictionaryEboReferences() {
178        return ConfigContext.getCurrentContextConfig().getBooleanProperty("validate.data.dictionary.ebo.references",
179                false);
180    }
181
182    public boolean isComponentPublishingEnabled() {
183        return ConfigContext.getCurrentContextConfig().getBooleanProperty(
184                KRADConstants.Config.COMPONENT_PUBLISHING_ENABLED, false);
185    }
186
187    public long getComponentPublishingDelay() {
188        return ConfigContext.getCurrentContextConfig().getNumericProperty(KRADConstants.Config.COMPONENT_PUBLISHING_DELAY, 0);
189    }
190
191    /**
192     * Used to "poke" the Data Dictionary again after the Spring Context is initialized.  This is to
193     * allow for modules loaded with KualiModule after the KNS has already been initialized to work.
194     *
195     * Also initializes the DateTimeService
196     */
197    protected void configureDataSource() {
198        if (getApplicationDataSource() != null) {
199            ConfigContext.getCurrentContextConfig()
200                    .putObject(KRADConstants.KRAD_APPLICATION_DATASOURCE, getApplicationDataSource());
201        }
202    }
203
204    public DataSource getApplicationDataSource() {
205        return this.applicationDataSource;
206    }
207
208    public void setApplicationDataSource(DataSource applicationDataSource) {
209        this.applicationDataSource = applicationDataSource;
210    }
211
212    /**
213     * Indicates whether the legacy KNS module should be included which will include
214     * the KNS spring beans file
215     *
216     * @return boolean true if kns should be supported, false if not
217     */
218    public boolean isIncludeKnsSpringBeans() {
219        return includeKnsSpringBeans;
220    }
221
222    /**
223     * Setter for the include kns support indicator
224     *
225     * @param includeKnsSpringBeans
226     */
227    public void setIncludeKnsSpringBeans(boolean includeKnsSpringBeans) {
228        this.includeKnsSpringBeans = includeKnsSpringBeans;
229    }
230}