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    }