001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004    
005      This file is part of Granite Data Services.
006    
007      Granite Data Services is free software; you can redistribute it and/or modify
008      it under the terms of the GNU Library General Public License as published by
009      the Free Software Foundation; either version 2 of the License, or (at your
010      option) any later version.
011    
012      Granite Data Services is distributed in the hope that it will be useful, but
013      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014      FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015      for more details.
016    
017      You should have received a copy of the GNU Library General Public License
018      along with this library; if not, see <http://www.gnu.org/licenses/>.
019    */
020    
021    package org.granite.tide.cdi;
022    
023    import java.lang.annotation.Annotation;
024    import java.lang.reflect.Method;
025    import java.lang.reflect.Type;
026    import java.util.ArrayList;
027    import java.util.HashSet;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.Set;
031    
032    import javax.annotation.PreDestroy;
033    import javax.enterprise.context.ContextNotActiveException;
034    import javax.enterprise.context.ConversationScoped;
035    import javax.enterprise.context.RequestScoped;
036    import javax.enterprise.context.SessionScoped;
037    import javax.enterprise.context.spi.CreationalContext;
038    import javax.enterprise.inject.Any;
039    import javax.enterprise.inject.Default;
040    import javax.enterprise.inject.spi.Bean;
041    import javax.enterprise.inject.spi.BeanManager;
042    import javax.enterprise.util.AnnotationLiteral;
043    import javax.inject.Inject;
044    import javax.servlet.http.HttpSession;
045    
046    import org.granite.config.GraniteConfig;
047    import org.granite.context.GraniteContext;
048    import org.granite.logging.Logger;
049    import org.granite.messaging.amf.io.util.ClassGetter;
050    import org.granite.messaging.service.ServiceInvocationContext;
051    import org.granite.messaging.webapp.HttpGraniteContext;
052    import org.granite.tide.IInvocationCall;
053    import org.granite.tide.IInvocationResult;
054    import org.granite.tide.TidePersistenceManager;
055    import org.granite.tide.TideServiceContext;
056    import org.granite.tide.annotations.BypassTideMerge;
057    import org.granite.tide.async.AsyncPublisher;
058    import org.granite.tide.cdi.lazy.CDIInitializer;
059    import org.granite.tide.data.DataContext;
060    import org.granite.tide.invocation.ContextEvent;
061    import org.granite.tide.invocation.ContextResult;
062    import org.granite.tide.invocation.ContextUpdate;
063    import org.granite.tide.invocation.InvocationCall;
064    import org.granite.tide.invocation.InvocationResult;
065    import org.granite.util.ClassUtil;
066    
067    
068    /**
069     * @author William DRAI
070     */
071    @SessionScoped
072    public class CDIServiceContext extends TideServiceContext {
073    
074        private static final long serialVersionUID = 1L;
075        
076        private static final Logger log = Logger.getLogger(CDIServiceContext.class);
077        
078        @SuppressWarnings("serial")
079        private static final AnnotationLiteral<Any> ANY_LITERAL = new AnnotationLiteral<Any>() {}; 
080        @SuppressWarnings("serial")
081        private static final AnnotationLiteral<Default> DEFAULT_LITERAL = new AnnotationLiteral<Default>() {}; 
082        
083        private @Inject BeanManager manager;
084        
085        private UserEvents userEvents;
086        private @Inject TideUserEvents tideUserEvents;
087        private boolean isAsynchronousContext = true;
088        private boolean isFirstCall = false;
089        private boolean isLogin = false;
090        private @Inject CDIInitializer tideEntityInitializer;
091        
092        
093        /**
094         * Determines the current sessionId for web invocations
095         */
096        @Override
097        public void initCall() {
098            super.initCall();
099            
100            if (userEvents != null)
101                return;
102            
103            if (getSessionId() != null)
104                userEvents = tideUserEvents.getUserEvents(getSessionId());
105            else {
106                GraniteContext graniteContext = GraniteContext.getCurrentInstance();
107                if (graniteContext instanceof HttpGraniteContext) {
108                    HttpSession session = ((HttpGraniteContext)graniteContext).getSession(false);
109                    if (session != null)
110                        setSessionId(session.getId());
111                    isAsynchronousContext = false;
112                }
113            }
114        }
115        
116        /**
117         * Initialize current sessionId and event listeners for this context
118         * 
119         * @param sessionId current sessionId
120         */
121        @Override
122        public void setSessionId(String sessionId) {
123            super.setSessionId(sessionId);
124            userEvents = tideUserEvents.getUserEvents(sessionId);
125        }
126        
127        /**
128         * Clear current session from user events registry
129         */
130        @PreDestroy
131        public void endSession() {
132            if (!isAsynchronousContext && getSessionId() != null)
133                tideUserEvents.unregisterSession(getSessionId());
134        }
135        
136        
137        @Inject
138        private ResultsEval resultsEval;
139        
140        private Map<ContextResult, Boolean> getResultsEval() {
141            return resultsEval.getResultsEval();
142        }
143        
144        
145    //    /**
146    //     * Constructs an asynchronous context object
147    //     * @return current context
148    //     */
149    //    public AsyncContext getAsyncContext() {
150    //      List<ContextResult> resultsEval = new ArrayList<ContextResult>();
151    //      for (ScopeType evalScopeType : EVAL_SCOPE_TYPES)
152    //              resultsEval.addAll(getResultsEval(evalScopeType).keySet());
153    //      
154    //        return new AsyncContext(getSessionId(), resultsEval);
155    //    }
156    //    
157    //    /**
158    //     * Restores an asynchronous context
159    //     * @param asyncContext saved context
160    //     */
161    //    public void setAsyncContext(AsyncContext asyncContext) {
162    //        AsyncPublisher asyncPublisher = getAsyncPublisher();
163    //        if (asyncPublisher != null)
164    //            asyncPublisher.initThread();
165    //        
166    //        Contexts.getSessionContext().set("org.jboss.seam.security.identity", asyncContext.getIdentity());
167    //        setSessionId(asyncContext.getSessionId());
168    //        for (ContextResult resultEval : asyncContext.getResults()) {
169    //              if (resultEval instanceof ScopedContextResult)
170    //                      getResultsEval(((ScopedContextResult)resultEval).getScope()).put(resultEval, Boolean.FALSE);
171    //              else
172    //                      getResultsEval(ScopeType.UNSPECIFIED).put(resultEval, Boolean.FALSE);
173    //        }
174    //    }
175        
176        
177        /**
178         * Implementation of component lookup for CDI service
179         * 
180         * @param componentName component name
181         */
182        @Override
183        public Object findComponent(String componentName, Class<?> componentClass) {
184            Bean<?> bean = findBean(componentName, componentClass);
185            if (bean == null)
186                    return null;
187            CreationalContext<?> cc = manager.createCreationalContext(bean);
188            return manager.getReference(bean, Object.class, cc);
189        }
190        
191        /**
192         * Implementation of component lookup for CDI service
193         * 
194         * @param componentName component name
195         */
196        @Override
197        public Set<Class<?>> findComponentClasses(String componentName, Class<?> componentClass) {
198            Bean<?> bean = findBean(componentName, componentClass);
199            return beanClasses(bean);
200        }
201        
202        private Bean<?> findBean(String componentName, Class<?> componentClass) {
203            if (componentClass != null) {
204                    Set<Bean<?>> beans = manager.getBeans(componentClass, ANY_LITERAL);
205                    // If only one match, return match
206                    if (beans.size() == 1)
207                            return beans.iterator().next();
208            }
209    
210            if (componentName != null && !("".equals(componentName))) {
211                    // If no previous match, return by name
212                    Set<Bean<?>> beans = manager.getBeans(componentName);
213                    if (!beans.isEmpty())
214                            return beans.iterator().next();
215            }
216            
217            if (componentClass != null) {
218                    // If more than one match and no named bean, return the Default one
219                    Set<Bean<?>> beans = manager.getBeans(componentClass, DEFAULT_LITERAL);
220                    if (beans.size() == 1)
221                            return beans.iterator().next();
222            }
223            
224            return null;
225        }
226        
227        private Set<Class<?>> beanClasses(Object bean) {
228            if (bean instanceof Bean<?>) {
229                    Set<Class<?>> classes = new HashSet<Class<?>>();
230                    for (Type type : ((Bean<?>)bean).getTypes()) {
231                            if (type instanceof Class<?>)
232                                    classes.add((Class<?>)type);
233                    }
234                    return classes;
235            }
236            
237            Set<Class<?>> classes = new HashSet<Class<?>>(1);
238            classes.add(bean.getClass());
239            return classes;
240        }
241        
242        
243    //    public void observeBeginConversation(@Observes org.jboss.webbeans.conversation.) {
244    //      Contexts.getEventContext().set("org.granite.tide.conversation.wasCreated", true);
245    //    }
246        
247        
248        /**
249         * Add an event in the current context
250         * 
251         * @param event the event
252         */
253        public void processEvent(Object event) {
254            // Add the event to the current invocation
255            TideInvocation tideInvocation = TideInvocation.get();
256            if (tideInvocation == null)
257                return;
258            
259            if (userEvents != null) {
260                String sessionId = getSessionId();
261                if (sessionId != null && userEvents.hasEventType(event.getClass()))
262                    tideInvocation.addEvent(new ContextEvent(event.getClass().getName(), 
263                                    new Object[] { event, null }));
264            }
265    //        else if (Contexts.getSessionContext().isSet("org.granite.seam.login")) {
266    //            // Force send of all events raised during login
267    //            tideInvocation.addEvent(new ContextEvent(type, params));
268    //        }
269        }
270        
271        
272        /**
273         * Factory for Seam async publisher
274         * 
275         * @return servlet context of the current application
276         */
277        @Override
278        protected AsyncPublisher getAsyncPublisher() {
279            return null;
280        }
281        
282        
283        @Inject
284        private ConversationState conversation;
285        
286        /**
287         * Synchronizes server context with data provided by the client
288         * 
289         * @param context invocation context
290         * @param c client call
291         * @param componentName name of the component which will be invoked
292         */
293        @Override
294        public void prepareCall(ServiceInvocationContext context, IInvocationCall c, String componentName, Class<?> componentClass) {
295            InvocationCall call = (InvocationCall)c;
296            List<String> listeners = call.getListeners();
297            List<ContextUpdate> updates = call.getUpdates();
298            Object[] results = call.getResults();
299            
300            try {
301                    if (manager.getContext(RequestScoped.class).isActive() && manager.getContext(SessionScoped.class).isActive() && isFirstCall && isLogin) {
302                        // Login tried : force evaluation of existing session context
303                        for (Map.Entry<ContextResult, Boolean> me : getResultsEval().entrySet()) {
304                            if (me.getKey().getExpression() == null 
305                                            && findBean(me.getKey().getComponentName(), me.getKey().getComponentClass()).getScope().equals(SessionScoped.class))
306                                me.setValue(Boolean.TRUE);
307                        }
308                        isLogin = false;
309                        isFirstCall = false;
310                    }
311            }
312            catch (ContextNotActiveException e) {
313                    // isActive() is not enough !!
314            }
315            try {
316                    if (manager.getContext(RequestScoped.class).isActive() && manager.getContext(ConversationScoped.class).isActive() 
317                            && conversation.isFirstCall()) { 
318                        // Join conversation : force evaluation of existing conversation context
319                        for (Map.Entry<ContextResult, Boolean> me : getResultsEval().entrySet()) {
320                            if (me.getKey().getExpression() == null 
321                                            && findBean(me.getKey().getComponentName(), me.getKey().getComponentClass()).getScope().equals(ConversationScoped.class))
322                                me.setValue(Boolean.TRUE);
323                        }
324                            conversation.setFirstCall(false);
325                    }
326            }
327            catch (ContextNotActiveException e) {
328                    // isActive() is not enough !!
329            }
330            
331            String sessionId = getSessionId();
332            if (sessionId != null && listeners != null) {
333                // Registers new event listeners
334                for (String listener : listeners) {
335                    try {
336                            Class<?> listenerClass = ClassUtil.forName(listener);
337                                    tideUserEvents.registerEventType(sessionId, listenerClass);
338                    }
339                    catch (ClassNotFoundException e) {
340                            log.error("Could not register event " + listener, e);
341                    }
342                }
343                
344                if (userEvents == null)
345                    userEvents = tideUserEvents.getUserEvents(getSessionId());
346            }
347            
348            if (results != null) {
349                    Map<ContextResult, Boolean> resultsEval = getResultsEval();
350                for (Object result : results) {
351                    ContextResult cr = (ContextResult)result;
352                    resultsEval.put(cr, Boolean.TRUE);
353                }
354            }
355            
356            try {
357                    tideEntityInitializer.restoreLoadedEntities();
358            }
359            catch (ContextNotActiveException e) {
360                    // Not in a conversation
361            }
362            
363            TideInvocation tideInvocation = TideInvocation.init();
364            tideInvocation.update(updates);
365        }
366    
367        /**
368         * Builds the result object for the invocation
369         * 
370         * @param context invocation context
371         * @param result result of the method invocation
372         * @param componentName name of the invoked component
373         * @return result object
374         */
375        @Override
376        public IInvocationResult postCall(ServiceInvocationContext context, Object result, String componentName, Class<?> componentClass) {
377            TideInvocation tideInvocation = TideInvocation.get();
378            int scope = 3;
379            boolean restrict = false;
380    
381                    List<ContextUpdate> results = new ArrayList<ContextUpdate>(tideInvocation.getResults());
382            DataContext dataContext = DataContext.get();
383                    Set<Object[]> dataUpdates = dataContext != null ? dataContext.getDataUpdates() : null;
384                    Object[][] updates = null;
385                    if (dataUpdates != null && !dataUpdates.isEmpty())
386                            updates = dataUpdates.toArray(new Object[dataUpdates.size()][]);
387                    
388            Bean<?> bean = null;
389            if (componentName != null || componentClass != null) {
390                // Determines scope of component
391                bean = findBean(componentName, componentClass);
392                if (bean.getScope() == RequestScoped.class)
393                    scope = 3;
394                else if (bean.getScope() == ConversationScoped.class)
395                    scope = 2;
396                else if (bean.getScope() == SessionScoped.class)
397                    scope = 1;
398                
399                try {
400                        if (manager.getContext(RequestScoped.class).get(bean) != null)
401                            scope = 3;
402                        else if (manager.getContext(ConversationScoped.class).get(bean) != null)
403                            scope = 2;
404                        else if (manager.getContext(SessionScoped.class).get(bean) != null)
405                            scope = 1;
406                }
407                catch (ContextNotActiveException e) {
408                    scope = 3;
409                }
410            }
411            
412            InvocationResult res = new InvocationResult(result, results);
413            res.setScope(scope);
414            res.setRestrict(restrict);
415            if (bean != null) {
416                    if (bean.getBeanClass().isAnnotationPresent(BypassTideMerge.class))
417                            res.setMerge(false);
418                    else {
419                            try {
420                                    if (context != null) {
421                                            Method m = bean.getBeanClass().getMethod(context.getMethod().getName(), context.getMethod().getParameterTypes());
422                                            if (m.isAnnotationPresent(BypassTideMerge.class))
423                                                    res.setMerge(false);
424                                    }
425                            }
426                            catch (Exception e) {
427                                    log.warn("Could not find bean method", e);
428                            }
429                    }
430            }
431            
432            res.setUpdates(updates);
433            
434            // Adds events in result object
435            res.setEvents(tideInvocation.getEvents());
436            
437            try {
438                    // Save current set of entities loaded in a conversation scoped component to handle case of extended PM
439                    tideEntityInitializer.saveLoadedEntities();
440            }
441            catch (ContextNotActiveException e) {
442                    // Not in a conversation
443            }
444    
445            // Clean thread
446            TideInvocation.remove();
447            
448            return res;
449        }
450    
451        /**
452         * Intercepts a fault on the invocation
453         * 
454         * @param context invocation context
455         * @param t exception thrown
456         * @param componentName name of the invoked component
457         */
458        @Override
459        public void postCallFault(ServiceInvocationContext context, Throwable t, String componentName, Class<?> componentClass) {
460            // Clean thread: very important to avoid phantom evaluations after exceptions
461            TideInvocation.remove();
462        }
463        
464        
465        public void addResultEval(ContextResult result) {
466            getResultsEval().put(result, Boolean.TRUE);
467        }
468        
469        
470        /**
471         * Evaluate updates in current server context
472         * 
473         * @param updates list of updates
474         * @param target the target instance
475         */
476        @SuppressWarnings("serial")
477        public void restoreContext(List<ContextUpdate> updates, Object target) {
478            if (updates == null)
479                return;
480            
481            GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig();
482            
483            // Restore context
484            for (ContextUpdate update : updates) {
485                
486                log.debug("Before invocation: evaluating expression #0.#1", update.getComponentName(), update.getExpression());
487                
488                Set<Bean<?>> beans = manager.getBeans(update.getValue().getClass(), new AnnotationLiteral<Any>() {});
489                if (beans.isEmpty() && update.getComponentName() != null)
490                    beans = manager.getBeans(update.getComponentName());
491                
492                if (!beans.isEmpty()) {
493                    Bean<?> sourceBean = beans.iterator().next();
494                
495                    Object previous = manager.getReference(sourceBean, Object.class, manager.createCreationalContext(sourceBean));
496                    boolean disabled = previous != null && sourceBean != null 
497                            && config.isComponentTideDisabled(sourceBean.getName(), beanClasses(sourceBean), previous);
498                        if (!disabled)
499                            mergeExternal(update.getValue(), previous);
500                }
501            }
502        }
503        
504        
505        /**
506         * Evaluate results from context
507         * 
508         * @param target the target instance
509         * @param nothing used by initializer to avoid interactions with context sync
510         * 
511         * @return list of updates to send back to the client
512         */
513        public List<ContextUpdate> evaluateResults(Object target, boolean nothing) {
514            
515            List<ContextUpdate> resultsMap = new ArrayList<ContextUpdate>();
516            
517            if (nothing)
518                return resultsMap;
519            
520            GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig();
521            ClassGetter classGetter = GraniteContext.getCurrentInstance().getGraniteConfig().getClassGetter();
522            
523            for (Map.Entry<ContextResult, Boolean> me : getResultsEval().entrySet()) {
524                if (!me.getValue())
525                    continue;
526                
527                ContextResult res = me.getKey();
528                
529                Class<?> componentClass = res.getComponentClass();
530                Bean<?> targetBean = findBean(res.getComponentName(), componentClass);
531                String targetComponentName = targetBean.getName();
532                
533                boolean add = true;
534                Class<? extends Annotation> scopeType = targetBean.getScope();
535                Boolean restrict = res.getRestrict();
536                
537                Object value = res instanceof ScopedContextResult 
538                    ? ((ScopedContextResult)res).getValue() 
539                    : manager.getReference(targetBean, Object.class, null);
540                
541                if (value != null && config.isComponentTideDisabled(targetComponentName, beanClasses(targetBean), value))
542                    add = false;
543                
544                if (add) {
545                    getResultsEval().put(res, false);
546                    
547                    if (value != null && classGetter != null) {
548                        classGetter.initialize(null, null, value);
549                        
550                        int scope = 3;
551                        if (scopeType == ConversationScoped.class)
552                            scope = 2;
553                        else if (scopeType == SessionScoped.class)
554                            scope = 1;
555                        
556                        resultsMap.add(new ContextUpdate(res.getComponentName(), null, value, scope, Boolean.TRUE.equals(restrict)));
557                        add = false;
558                    }
559                }
560                
561                me.setValue(Boolean.FALSE);
562            }
563            
564            return resultsMap;
565        }
566    
567            @Inject
568            private CDIInitializer initializer;
569            
570            @Override
571            protected TidePersistenceManager getTidePersistenceManager(boolean create) {
572                    return initializer.getPersistenceManager();
573            }
574            
575            
576            @Override
577        protected boolean equals(Object obj1, Object obj2) {
578                    if (super.equals(obj1, obj2))
579                            return true;
580                    
581                    return (obj1 != null && obj2 != null 
582                                    && (obj1.getClass().isAnnotationPresent(TideBean.class) || obj2.getClass().isAnnotationPresent(TideBean.class)));
583        }
584        
585        
586    //    /**
587    //     * Implementations of intercepted asynchronous calls
588    //     * Send asynchronous event to client
589    //     * @param asyncContext current context (session id)
590    //     * @param targetComponentName target component name
591    //     * @param methodName method name
592    //     * @param paramTypes method argument types
593    //     * @param params argument values
594    //     * @return result
595    //     */
596    //    public Object invokeAsynchronous(AsyncContext asyncContext, String targetComponentName, String methodName, Class<?>[] paramTypes, Object[] params) {
597    //        setAsyncContext(asyncContext);
598    //        
599    //        // Just another ugly hack: the Seam interceptor has set this variable and we don't want it
600    //        Contexts.getEventContext().remove("org.jboss.seam.async.AsynchronousIntercepter.REENTRANT");
601    //        
602    //        Component component = TideInit.lookupComponent(targetComponentName);
603    //        
604    //        // Forces evaluation of all results if they are related to the called component
605    //        for (Map.Entry<ContextResult, Boolean> me : getResultsEval(component.getScope()).entrySet()) {
606    //            if (me.getKey().getComponentName().equals(targetComponentName))
607    //                me.setValue(Boolean.TRUE);
608    //        }
609    //        
610    //        Object target = Component.getInstance(targetComponentName);
611    //        
612    //        Method method;
613    //        try {
614    //            method = target.getClass().getMethod(methodName, paramTypes);
615    //        }
616    //        catch (NoSuchMethodException nsme) {
617    //           throw new IllegalStateException(nsme);
618    //        }
619    //        
620    //        Object result = Reflections.invokeAndWrap(method, target, params);
621    //        
622    //        sendEvent(targetComponentName);
623    //        
624    //        return result;
625    //    }
626    //    
627    //
628    //    /**
629    //     * Search for a named attribute in all contexts, in the
630    //     * following order: method, event, page, conversation,
631    //     * session, business process, application.
632    //     * 
633    //     * @return the first component found, or null
634    //     */
635    //    public static Object[] lookupInStatefulContexts(String name, ScopeType scope) {
636    //        if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.METHOD.equals(scope)) && Contexts.isMethodContextActive()) {
637    //            Object result = Contexts.getMethodContext().get(name);
638    //            if (result != null)
639    //                return new Object[] { result, Contexts.getMethodContext().getType() };
640    //        }
641    //        
642    //        if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.EVENT.equals(scope)) && Contexts.isEventContextActive()) {
643    //            Object result = Contexts.getEventContext().get(name);
644    //            if (result != null)
645    //                return new Object[] { result, Contexts.getEventContext().getType() };
646    //        }
647    //        
648    //        if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.PAGE.equals(scope)) && Contexts.isPageContextActive()) {
649    //            Object result = Contexts.getPageContext().get(name);
650    //            if (result != null)
651    //                return new Object[] { result, Contexts.getPageContext().getType() };
652    //        }
653    //        
654    //        if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.CONVERSATION.equals(scope)) && Contexts.isConversationContextActive()) {
655    //            Object result = Contexts.getConversationContext().get(name);
656    //            if (result != null)
657    //                return new Object[] { result, Contexts.getConversationContext().getType() };
658    //        }
659    //        
660    //        if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.SESSION.equals(scope)) && Contexts.isSessionContextActive()) {
661    //            Object result = Contexts.getSessionContext().get(name);
662    //            if (result != null)
663    //                return new Object[] { result, Contexts.getSessionContext().getType() };
664    //        }
665    //        
666    //        if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.BUSINESS_PROCESS.equals(scope)) && Contexts.isBusinessProcessContextActive()) {
667    //            Object result = Contexts.getBusinessProcessContext().get(name);
668    //            if (result != null)
669    //                return new Object[] { result, Contexts.getBusinessProcessContext().getType() };
670    //        }
671    //        
672    //        if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.APPLICATION.equals(scope)) && Contexts.isApplicationContextActive()) {
673    //            Object result = Contexts.getApplicationContext().get(name);
674    //            if (result != null)
675    //                return new Object[] { result, Contexts.getApplicationContext().getType() };
676    //        }
677    //        
678    //        return ScopeType.UNSPECIFIED.equals(scope) ? null : new Object[] { null, scope };
679    //    }
680    }