001 package com.hammurapi.review;
002
003 import java.net.URL;
004 import java.net.URLClassLoader;
005 import java.util.ArrayList;
006 import java.util.Iterator;
007 import java.util.List;
008 import java.util.concurrent.Callable;
009 import java.util.concurrent.Executor;
010 import java.util.concurrent.Future;
011 import java.util.logging.Logger;
012
013 import org.eclipse.emf.common.util.TreeIterator;
014
015 import com.hammurapi.config.Factory;
016 import com.hammurapi.config.Path;
017 import com.hammurapi.config.bootstrap.ConfigurationException;
018 import com.hammurapi.config.bootstrap.Destroyable;
019 import com.hammurapi.config.bootstrap.FactoryClosure;
020 import com.hammurapi.config.runtime.CompositeDestroyable;
021 import com.hammurapi.config.runtime.ConfigurationContext;
022 import com.hammurapi.config.runtime.DestroyableSink;
023 import com.hammurapi.config.runtime.FactoryConfig;
024 import com.hammurapi.convert.ConvertingService;
025 import com.hammurapi.reasoning.ExceptionHandler;
026 import com.hammurapi.reasoning.ForwardReasoningSession;
027 import com.hammurapi.reasoning.ForwardReasoningSessionFactory;
028 import com.hammurapi.reasoning.ReasoningException;
029 import com.hammurapi.util.InlineExecutor;
030 import com.hammurapi.util.SingletonChainingContext;
031 import com.hammurapi.util.concurrent.NonBlockingFutureTask;
032
033 /**
034 * This class performs reviews invoking other review framework components.
035 * @author Pavel Vlasov
036 *
037 */
038 public abstract class ReviewDriverBase implements Callable<Object>, com.hammurapi.config.runtime.Component<Component> {
039
040 private static final Logger logger = Logger.getLogger(ReviewDriverBase.class.getName());
041
042 private Executor executor;
043 private ConfigurationContext<Component> context;
044
045 @Override
046 public void init(ConfigurationContext<Component> context) throws ConfigurationException {
047 executor = context.lookup(Executor.class);
048 if (executor==null) {
049 executor = InlineExecutor.INSTANCE;
050 }
051 this.context = context;
052 }
053
054 /**
055 * Loads component definition resource and executes review.
056 * @param componentConfig
057 * @throws ConfigurationException
058 */
059 @SuppressWarnings("unchecked")
060 @Override
061 public Object call() throws Exception {
062 CompositeDestroyable destroyableSink = new CompositeDestroyable();
063 try {
064 // Create reporters
065 List<Reporter<Object>> reporters = new ArrayList<Reporter<Object>>();
066 for (Factory rf: context.getDefinition().getReporter()) {
067 FactoryClosure<Object> rfr = rf.create(context.getFactoryConfig());
068 logger.fine("Creating a reporter");
069 reporters.add((Reporter<Object>) rfr.create());
070 destroyableSink.addDestroyable(rfr.getDestroyable());
071 }
072
073 try {
074 List<Future<Object>> tasks = new ArrayList<Future<Object>>();
075 // Review modules
076 for (Module module: context.getDefinition().getModule()) {
077 NonBlockingFutureTask<Object> task = new NonBlockingFutureTask<Object>(createModuleTask(module, reporters, context.getFactoryConfig(), destroyableSink, executor));
078 tasks.add(task);
079 executor.execute(task);
080 }
081
082 for (Future<Object> task: tasks) {
083 try {
084 task.get();
085 } catch (Exception e) {
086 for (Reporter r: reporters) {
087 r.onException(e);
088 }
089 }
090 }
091 } finally {
092 for (Reporter<Object> r: reporters) {
093 r.close();
094 }
095 }
096 } finally {
097 destroyableSink.destroy();
098 }
099 return null;
100 }
101
102 private Callable<Object> createModuleTask(
103 final Module module,
104 final List<Reporter<Object>> reporters,
105 final FactoryConfig factoryConfig,
106 final DestroyableSink destroyableSink,
107 final Executor executor) {
108
109 logger.fine("Creating a module task for "+module.getName());
110
111 return new Callable<Object>() {
112
113 @SuppressWarnings("unchecked")
114 @Override
115 public Object call() throws Exception {
116 logger.fine("Executing module task for "+module.getName());
117
118 // Classpath
119 List<URL> pathElements = new ArrayList<URL>();
120 for (Path path: module.getClassPath()) {
121 pathElements.addAll(path.getUrls(factoryConfig.getContextUrl(), factoryConfig.getTokenSource()));
122 }
123
124 ClassLoader classLoader = factoryConfig.getClassLoader();
125 if (classLoader==null) {
126 classLoader = getClass().getClassLoader();
127 }
128
129 if (!pathElements.isEmpty()) {
130 logger.fine("Constructing module URL classloader from "+pathElements);
131 classLoader = new URLClassLoader(pathElements.toArray(new URL[pathElements.size()]), classLoader);
132 }
133
134 FactoryConfig moduleFactoryConfig = (FactoryConfig) factoryConfig.clone();
135 moduleFactoryConfig.setClassLoader(classLoader);
136
137 final List<ForwardReasoningSessionFactory<Object>> inspectorSetSessionFactories = new ArrayList<ForwardReasoningSessionFactory<Object>>();
138 for (InspectorSet inspectorSet: module.getInspectorSet()) {
139 inspectorSetSessionFactories.add(createInspectorSetSessionFactory(inspectorSet, moduleFactoryConfig));
140 }
141
142 try {
143 logger.fine("Reviewing module sources: "+module.getSource().size());
144 for (Factory sf: module.getSource()) {
145 logger.fine("Reviewing module source");
146 final CompositeObservationSink observationSink = new CompositeObservationSink();
147 for (Reporter<Object> r: reporters) {
148 observationSink.addSink(r.createObservationSink(module));
149 }
150
151 FactoryConfig modelConfig = (FactoryConfig) moduleFactoryConfig.clone();
152 modelConfig.setContext(new SingletonChainingContext<ObservationSink>(ObservationSink.class, observationSink, modelConfig.getContext()));
153 FactoryClosure<Object> sfr = sf.create(modelConfig);
154 Object model = sfr.create();
155 module.getModel().add(model);
156 destroyableSink.addDestroyable(sfr.getDestroyable());
157
158 List<Future<Object>> tasks = new ArrayList<Future<Object>>();
159 Iterable<Object> modelIterable = ConvertingService.convert(model, Iterable.class);
160 if (modelIterable==null) {
161 Warning warning = ReviewFactory.eINSTANCE.createWarning();
162 warning.setMessage(model.getClass()+" cannot be converted to Iterable");
163 observationSink.consumeObservation(warning);
164 } else {
165 ExceptionHandler exceptionHandler = new ExceptionHandler() {
166
167 @Override
168 public void handleException(Exception e) throws Exception {
169 Warning warning = ReviewFactory.eINSTANCE.createWarning();
170 warning.setMessage("Exception during inspector execution");
171 warning.setCause(e);
172 observationSink.consumeObservation(warning);
173 }
174 };
175 List<ForwardReasoningSession<Object>> moduleSessions = new ArrayList<ForwardReasoningSession<Object>>();
176 for (ForwardReasoningSessionFactory<Object> issf: inspectorSetSessionFactories) {
177 logger.fine("Creating module session");
178 ForwardReasoningSession<Object> session = issf.createSession(null);
179 session.setExceptionHandler(exceptionHandler);
180 moduleSessions.add(session); // TODO - session properties.
181 }
182 try {
183 // Collect observations from the model loading
184 Iterator<Object> modelIterator = modelIterable.iterator();
185 while (modelIterator.hasNext()) {
186 Object modelElement = modelIterator.next();
187 if (modelElement instanceof LanguageElement) {
188 for (Observation observation: ((LanguageElement) modelElement).getObservations()) {
189 observationSink.consumeObservation(observation);
190 }
191 }
192 }
193
194 // Give the model to inspectors.
195 modelIterator = modelIterable.iterator();
196 while (modelIterator.hasNext()) {
197 review(modelIterator, tasks, executor, moduleSessions, inspectorSetSessionFactories, observationSink);
198 }
199
200 // Collect observations from sessions
201 for (ForwardReasoningSession<Object> frs: moduleSessions) {
202 frs.executeRules();
203 for (Object fact: frs.getObjects()) {
204 if (fact instanceof Observation) {
205 observationSink.consumeObservation((Observation) fact);
206 }
207 }
208 }
209
210 } finally {
211 for (ForwardReasoningSession<Object> frs: moduleSessions) {
212 frs.close();
213 }
214 }
215 }
216
217 // Wait for review tasks to finish
218 for (Future<Object> task: tasks) {
219 task.get();
220 }
221
222 // Close sinks
223 observationSink.close();
224 }
225
226 return null;
227 } finally {
228 for (ForwardReasoningSessionFactory<Object> issf: inspectorSetSessionFactories) {
229 if (issf instanceof Destroyable) {
230 ((Destroyable) issf).destroy();
231 }
232 }
233 }
234 }
235
236 };
237 }
238
239 /**
240 * Reviews model element, possibly in a different thread.
241 * @param modelElement
242 * @param inspectorSetSessionFactories
243 * @param moduleSessions
244 * @param counter
245 * @param executor
246 * @param observationSinks
247 * @throws ReasoningException
248 */
249 @SuppressWarnings("unchecked")
250 private void review(
251 Iterator<Object> modelIterator,
252 final List<Future<Object>> tasks,
253 final Executor executor,
254 List<ForwardReasoningSession<Object>> sessions,
255 final List<ForwardReasoningSessionFactory<Object>> inspectorSetSessionFactories,
256 final ObservationSink observationSink) {
257
258 final Object modelElement = modelIterator.next();
259 if (modelElement!=null) {
260 if (modelIterator instanceof TreeIterator<?> && modelElement.getClass().getAnnotation(Fork.class)!=null) {
261 // Reviewing model element in a separate thread
262 final Iterable<Object> meIterable = ConvertingService.convert(modelElement, Iterable.class);
263 if (meIterable!=null) {
264 ((TreeIterator<?>) modelIterator).prune();
265 Runnable reviewTask = new Runnable() {
266
267 @Override
268 public void run() {
269 try {
270 List<ForwardReasoningSession<Object>> meSessions = new ArrayList<ForwardReasoningSession<Object>>();
271 for (ForwardReasoningSessionFactory<Object> issf: inspectorSetSessionFactories) {
272 meSessions.add(issf.createSession(null)); // TODO - session properties.
273 }
274 try {
275 Iterator<Object> modelIterator = meIterable.iterator();
276 while (modelIterator.hasNext()) {
277 review(modelIterator, tasks, executor, meSessions, inspectorSetSessionFactories, observationSink);
278 }
279 // Collect observations
280 for (ForwardReasoningSession<Object> frs: meSessions) {
281 frs.executeRules();
282 for (Object fact: frs.getObjects()) {
283 if (fact instanceof Observation) {
284 observationSink.consumeObservation((Observation) fact);
285 }
286 }
287 }
288
289 } finally {
290 for (ForwardReasoningSession<Object> frs: meSessions) {
291 frs.close();
292 }
293 }
294 } catch (Exception e) {
295 Warning warning = ReviewFactory.eINSTANCE.createWarning();
296 warning.setMessage("Exception: "+e);
297 warning.setCause(e);
298 warning.setSource(ConvertingService.convert(modelElement, LanguageElement.class));
299 }
300 }
301
302 };
303
304 executor.execute(new NonBlockingFutureTask<Object>(reviewTask, Boolean.TRUE));
305 return;
306 }
307 }
308
309 for (ForwardReasoningSession<Object> frs: sessions) {
310 try {
311 frs.put(modelElement);
312 } catch (ReasoningException e) {
313 Warning warning = ReviewFactory.eINSTANCE.createWarning();
314 warning.setMessage("Exception: "+e);
315 warning.setCause(e);
316 warning.setSource(ConvertingService.convert(modelElement, LanguageElement.class));
317 }
318 }
319 }
320 }
321
322 protected abstract ForwardReasoningSessionFactory<Object> createInspectorSetSessionFactory(InspectorSet inspectorSet, FactoryConfig moduleFactoryConfig) throws ConfigurationException, ReasoningException;
323 }