001: /*
002: * Copyright 2005-2006 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package edu.iu.uis.eden;
018:
019: import java.util.HashSet;
020: import java.util.Set;
021:
022: import javax.xml.namespace.QName;
023:
024: import org.apache.log4j.Logger;
025: import org.kuali.rice.core.Core;
026: import org.kuali.rice.lifecycle.BaseLifecycle;
027: import org.kuali.rice.resourceloader.ServiceLocator;
028: import org.springframework.context.ApplicationContext;
029: import org.springframework.context.ConfigurableApplicationContext;
030: import org.springframework.context.support.ClassPathXmlApplicationContext;
031:
032: import edu.iu.uis.eden.core.ContextualConfigLock;
033:
034: /**
035: * ServiceLocator that starts and wraps the primary workflow Spring Application Context.
036: *
037: * @author ewestfal
038: * @author rkirkend
039: *
040: */
041: public final class SpringLoader extends BaseLifecycle implements
042: ServiceLocator {
043:
044: private static final Logger LOG = Logger
045: .getLogger(SpringLoader.class);
046:
047: /**
048: * The default top-level Spring configuration resource name
049: */
050: public static final String DEFAULT_SPRING_FILE = "Spring.xml";
051:
052: /* Conditions are used to ensure state integrity. Various methods that modify state
053: * are already synchronized so in some places these are redundant. At some point we
054: * should switch to java.util.concurrent Lock and Condition classes and implement this
055: * correctly.
056: */
057:
058: private static final SpringLoader INSTANCE = new SpringLoader();
059:
060: /**
061: * Condition that indicates whether Spring has completed initialization
062: */
063: private final ContextualConfigLock SPRING_INIT_COMPLETE = new ContextualConfigLock(
064: "Spring has completed initialization");
065: /**
066: * Condition that indicates whether Spring has started initialization
067: */
068: private final ContextualConfigLock SPRING_INIT_STARTED = new ContextualConfigLock(
069: "Spring has started initialization");
070:
071: private String contextFile = DEFAULT_SPRING_FILE;
072: private static Set<String> suppressedServices = null;
073:
074: /**
075: * The Spring context
076: */
077: private ConfigurableApplicationContext appContext;
078:
079: public static SpringLoader getInstance() {
080: return INSTANCE;
081: }
082:
083: public synchronized void start() throws Exception {
084: initializeAppContexts();
085: super .start();
086: }
087:
088: public synchronized void stop() throws Exception {
089: close();
090: super .stop();
091: }
092:
093: /**
094: * Initializes Spring with the specified context resource name
095: * @param rootContextFile the Spring context resource name
096: */
097: protected void initializeAppContexts() {
098: if (SPRING_INIT_STARTED.hasFired()
099: || SPRING_INIT_COMPLETE.hasFired()) {
100: return;
101: }
102: SPRING_INIT_STARTED.fire();
103:
104: LOG.info("Initializing Spring from resource: "
105: + getContextFile());
106: try {
107: appContext = new ClassPathXmlApplicationContext(
108: getContextFile());
109: appContext.getBeanFactory().preInstantiateSingletons();
110: } finally {
111: // if an exception occurs we need to signal that init is complete
112: // even though it is in error, so that close will be allowed
113: SPRING_INIT_COMPLETE.fire();
114: }
115: }
116:
117: public Object getService(QName qname) {
118: return getBean(qname.toString());
119: }
120:
121: /**
122: * Obtains a bean from Spring
123: *
124: * @param serviceName the name of the bean
125: * @return a bean from Spring
126: */
127: public Object getBean(String serviceName) {
128: initializeAppContexts();
129:
130: if (appContext == null) {
131: throw new RuntimeException(
132: "Spring not initialized properly. Initialization has completed and the application context is null.");
133: }
134: if (appContext.containsBean(serviceName)) {
135: return appContext.getBean(serviceName);
136: }
137: return null;
138: }
139:
140: public Class getType(String beanName) {
141: initializeAppContexts();
142: if (appContext == null) {
143: throw new RuntimeException(
144: "Spring not initialized properly. Initialization has completed and the application context is null.");
145: }
146: return appContext.getType(beanName);
147: }
148:
149: public boolean isSingleton(String beanName) {
150: initializeAppContexts();
151: if (appContext == null) {
152: throw new RuntimeException(
153: "Spring not initialized properly. Initialization has completed and the application context is null.");
154: }
155: return appContext.isSingleton(beanName);
156: }
157:
158: /**
159: * Closes the Spring context if initialization has at least started.
160: * If initialization has NOT started, this method returns immediately.
161: * If initialization HAS started, then it awaits completion before closing
162: */
163: public synchronized void close() {
164: if (!SPRING_INIT_STARTED.hasFired()) {
165: return;
166: }
167: // this is not technically necessary at this time because both close and initialize* methods
168: // are synchronized so initialization should always be complete if it has been started;
169: // NOTE: although - let's think about error cases...if exception is thrown from init, then
170: // SPRING_INIT_COMPLETE may never have fired - that is a good thing probably, but maybe it warrants
171: // a separate SPRING_INIT_FAILED condition; don't want to get too complicated though
172: SPRING_INIT_COMPLETE.await();
173: if (appContext instanceof ConfigurableApplicationContext) {
174: ((ConfigurableApplicationContext) appContext).close();
175: }
176: SPRING_INIT_STARTED.reset();
177: SPRING_INIT_COMPLETE.reset();
178: this .suppressedServices = null;
179: }
180:
181: /**
182: * Returns the Spring context, waiting for initialization to start and complete
183: * if it hasn't already
184: * @return the intiailized Spring context
185: */
186: public ApplicationContext getApplicationContext() {
187: SPRING_INIT_COMPLETE.await();
188: return appContext;
189: }
190:
191: public String getContents(String indent, boolean servicePerLine) {
192: String contents = indent + "SpringLoader " + this
193: + " services =";
194:
195: for (String beanName : appContext.getBeanDefinitionNames()) {
196: if (servicePerLine) {
197: contents += indent + "+++" + beanName + "\n";
198: } else {
199: contents += beanName + ", ";
200: }
201: }
202:
203: return contents;
204: }
205:
206: public String getContextFile() {
207: return contextFile;
208: }
209:
210: public void setContextFile(String contextFile) {
211: this.contextFile = contextFile;
212: }
213:
214: }
|