天天看點

e4中的org.eclipse.e4.core.contexts.IContextFunction

(轉載自:http://414149609.iteye.com/blog/1646251

這個實驗很有意思,簡單有效地證明了EclipseContext每次取一個值之前都會執行compute方法。實際e4 RCP開發過程中,常常結合Declarative Service來充分發揮e4的Dependency Injection的作用。簡單來說就是當一個類的構造函數有@inject的标簽,e4就會從EclipseContext裡面尋找符合的key,然後通過compute找出對象值,然後inject給構造函數。在Declarative Service中,把接口注冊在service.context.key屬性中,把實作類bind到 org.eclipse.e4.core.contexts.IContextFunction 服務,這樣一個标有inject的構造函數需要這個接口作為變量時,e4會找到對應的實作類。這是e4的衆多美妙之處之一。)

相信用過E4的EclipseContext的人都知道,EclipseContext是一個可以類似java的Map一樣存儲資料(key-value),而且EclipseContext還可以有父子關系,這裡不詳談。 

EclipseContext 存儲key-value的時候,這個value可以是動态變化的,也就是可以算出來的。這點跟java的map有很大差别。在java裡面一旦建立key和value的關系,除非對這個再進行put或者remove的操作,否則取出來的value都是同一個對象。而EclipseContext 提供了一個IContextFunction的接口,這個接口代碼如下: 

/******************************************************************************* 
 * Copyright (c) 2009, 2010 IBM Corporation and others. 
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution, and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 * 
 * Contributors: 
 *     IBM Corporation - initial API and implementation 
 *******************************************************************************/  
  
package org.eclipse.e4.core.contexts;  
  
import org.osgi.framework.BundleContext;  
  
/** 
 * A context function encapsulates evaluation of some code within an 
 * {@link IEclipseContext}. The result of the function must be derived purely 
 * from the provided arguments and context objects, and must be free from 
 * side-effects other than the function's return value. In particular, the 
 * function must be idempotent - subsequent invocations of the same function 
 * with the same inputs must produce the same result. 
 * <p> 
 * A common use for context functions is as a place holder for an object that 
 * has not yet been created. These place holders can be stored as values in an 
 * {@link IEclipseContext}, allowing the concrete value they represent to be 
 * computed lazily only when requested. 
 * </p> 
 * <p> 
 * Context functions can optionally be registered as OSGi services. Context 
 * implementations may use such registered services to seed context instances 
 * with initial values. Registering your context function as a service is a 
 * signal that contexts are free to add an instance of your function to their 
 * context automatically, using the key specified by the 
 * {@link #SERVICE_CONTEXT_KEY} service property. 
 * </p> 
 *  
 * @see IEclipseContext#set(String, Object) 
 * @noimplement This interface is not intended to be implemented by clients. 
 *              Function implementations must subclass {@link ContextFunction} 
 *              instead. 
 */  
public interface IContextFunction {  
    /** 
     * The OSGi service name for a context function service. This name can be 
     * used to obtain instances of the service. 
     *  
     * @see BundleContext#getServiceReference(String) 
     */  
    public static final String SERVICE_NAME = IContextFunction.class.getName();  
  
    /** 
     * An OSGi service property used to indicate the context key this function 
     * should be registered in. 
     *  
     * @see BundleContext#getServiceReference(String) 
     */  
    public static final String SERVICE_CONTEXT_KEY = "service.context.key"; //$NON-NLS-1$  
  
    /** 
     * Evaluates the function based on the provided arguments and context to 
     * produce a consistent result. 
     *  
     * @param context 
     *            The context in which to perform the value computation. 
     * @return The concrete value. 
     */  
    public Object compute(IEclipseContext context);  
  
}  
           

下面代碼示範一個使用ContextFunction的簡單例子,Jone每隔一段時間去檢查一下這個key為"WEATHER"的value,每過一段時間會傳回不一樣的天氣

package teste4;  
  
import java.util.Random;  
  
import org.eclipse.e4.core.contexts.ContextFunction;  
import org.eclipse.e4.core.contexts.EclipseContextFactory;  
import org.eclipse.e4.core.contexts.IEclipseContext;  
import org.eclipse.e4.core.internal.contexts.ContextChangeEvent;  
import org.eclipse.e4.core.internal.contexts.EclipseContext;  
import org.eclipse.equinox.app.IApplication;  
import org.eclipse.equinox.app.IApplicationContext;  
  
/** 
 * This class controls all aspects of the application's execution 
 */  
public class Application implements IApplication {  
  
    EclipseContext ec = null;  
    public static String weather = "sunshine";  
  
    public Object start(IApplicationContext context) throws Exception {  
        System.out.println("Hello RCP World!");  
        // System.out.println(context);  
        ec = (EclipseContext) EclipseContextFactory.create("root");  
        ec.set("WEATHER", new ContextFunction() {  
            public Object compute(final IEclipseContext context) {  
                return weather;  
            }  
        });  
        Thread thread1 = new Thread(Jone);  
        Thread thread2 = new Thread(WeatherReport);  
        thread1.setName("Jone");  
        thread1.start();  
        thread2.start();  
        return IApplication.EXIT_OK;  
    }  
  
    Runnable Jone = new Runnable() {  
  
        public void run() {  
            // 每過0.25秒檢查目前的天氣  
            while (true) {  
                try {  
                    Thread.sleep(250);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                System.out.println(checkWeather());  
  
            }  
        }  
  
        String checkWeather() {  
            return (String) ec.get("WEATHER");  
        }  
    };  
  
    Runnable WeatherReport = new Runnable() {  
        Random r = new Random(5);  
  
        @Override  
        public void run() {  
            // 每過0.5秒,更新一個天氣  
            while (true) {  
                switch (r.nextInt(5)) {  
                case 1:  
                    weather = "sunshine";  
                    break;  
                case 2:  
                    weather = "cloudy";  
                    break;  
                case 3:  
                    weather = "rainy";  
                    break;  
                case 4:  
                    weather = "windy";  
                    break;  
                default:  
                    weather = "very bad";  
                    break;  
                }  
                // /告訴要重新執行一次key="WEATHER" 對應的ContextFunction的compute方法  
                ec.invalidate("WEATHER", ContextChangeEvent.UPDATE, "", null);  
  
                try {  
                    Thread.sleep(500);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    };  
  
    public void stop() {  
    }  
}  
           

輸出如下: 

Hello RCP World! 

cloudy 

cloudy 

cloudy 

cloudy 

windy 

windy 

windy 

sunshine 

sunshine 

sunshine 

very bad 

very bad 

windy 

sunshine 

sunshine 

sunshine 

cloudy 

sunshine 

sunshine 

sunshine 

sunshine 

rainy 

rainy 

rainy 

cloudy 

very bad 

very bad 

very bad 

繼續閱讀