View Javadoc

1   /*
2   Copyright (C) 2000 - 2007 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  package com.gridsystems.innergrid.api;
18  
19  import java.io.InputStream;
20  import java.io.Serializable;
21  import java.lang.reflect.Constructor;
22  import java.net.URL;
23  import java.rmi.Remote;
24  import java.util.Comparator;
25  import java.util.Enumeration;
26  import java.util.Map;
27  import java.util.Properties;
28  import java.util.TreeMap;
29  
30  import com.gridsystems.innergrid.kernel.KernelException;
31  
32  /**
33   * Helper class able to create API instances, through {@link #newApi newApi}.
34   *
35   * @author Xmas
36   * @version 1.0
37   */
38  public final class ApiFactory {
39  
40    /**
41     * Port for the connection used during discovery.
42     */
43    private static final int DUMMY_DEFAULT_PORT = 1;
44  
45    /**
46     * API internal cache.
47     */
48    private static Map<Class, Api> apis = new TreeMap<Class, Api>(new ClassComparator());
49  
50    /**
51     * Interface internal cache.
52     */
53    private static Map<Class, Class> interfaces
54      = new TreeMap<Class, Class>(new ClassComparator());
55  
56    /**
57     * Utility class: no need to instantiate it.
58     */
59    private ApiFactory() { }
60  
61    /*
62     * Loading all ApiProviders in the classpath. Discovery is performed by reading
63     * all resources with name "com/gridsystems/innergrid/api/api.providers" in the
64     * classpath.
65     */
66    static {
67      String apiname = "com/gridsystems/innergrid/api/api.providers";
68  
69      //Location of the description files of Api providers
70  
71      Enumeration enumeration = null;
72      try {
73        ClassLoader cl = Thread.currentThread().getContextClassLoader();
74        enumeration = cl.getResources(apiname);
75      } catch (Exception ex) {
76        System.err.println("ApiFactory::static() -> Error loading API.");
77        enumeration = null;
78      }
79  
80      Connection conn = null;
81      try {
82        Credentials cred = new UsernameTokenCredentials("", "");
83        conn = new DirectConnection("localhost", DUMMY_DEFAULT_PORT, true, cred);
84      } catch (Exception e) { }
85  
86      while (enumeration.hasMoreElements()) {
87        URL resource = (URL)enumeration.nextElement();
88        parseResource(resource, conn);
89      }
90    };
91  
92    /**
93     * Parsing a single resource and registering all declared API providers.
94     *
95     * @param resource the resource to parse
96     * @param c        the connection to use for instantiation
97     */
98    private static void parseResource(URL resource, Connection c) {
99      try {
100       InputStream is = resource.openStream();
101 
102       Properties props = new Properties();
103       props.load(is);
104 
105       Enumeration e = props.keys();
106 
107       while (e.hasMoreElements()) {
108         String key = (String)e.nextElement();
109         String value = props.getProperty(key);
110         parseResourceKeyValue(key, value, c);
111       }
112     } catch (Exception ex) { }
113   }
114 
115   /**
116    * Parsing a single key-value pair in a resource, and if it declares a valid API
117    * provider, it registers it.
118    *
119    * @param key   the key
120    * @param value the value
121    * @param c     the connection to use for instantiation
122    */
123   private static void parseResourceKeyValue(String key, String value, Connection c) {
124     String implName = key;
125     String ifaceName = value;
126     boolean isRemote = true;
127 
128     int pos = value.indexOf(",");
129     if (pos != -1) {
130       ifaceName = value.substring(0, pos);
131       isRemote = value.substring(pos + 1, value.length()).trim().equals("remote");
132     }
133 
134     // Loading implementation Class
135     Class< ? > impl;
136     try {
137       impl = Class.forName(implName);
138       Constructor cons = impl.getConstructor(new Class[] {Connection.class});
139       cons.newInstance(new Object[] {c});
140     } catch (ClassNotFoundException ex) {
141       System.err.println("Cannot find " + implName + " implementation class");
142       return;
143     } catch (Exception e) {
144       System.err.println("Cannot find constructor with Connection "
145                          + "parameter in " + implName + " class");
146       return;
147     }
148 
149     //  Loading interface Class
150     Class iface;
151     try {
152       iface = Class.forName(ifaceName);
153     } catch (Exception ex) {
154       System.err.println("Cannot find " + ifaceName + " interface");
155       return;
156     }
157 
158     // Adding
159     try {
160       addApi(iface, impl, isRemote);
161     } catch (Exception ex) {
162       System.err.println("Cannot add api to internal list");
163     }
164   }
165 
166   /**
167    * Compares two class names lexicographically, not taking into account the case.
168    *
169    * @author Xmas
170    * @version 1.0
171    */
172   private static class ClassComparator implements Comparator<Class>, Serializable {
173 
174     /**
175      * Serial Version UID.
176      */
177     private static final long serialVersionUID = 1234523452345L;
178 
179     /**
180      * Default constructor.
181      */
182     public ClassComparator() { }
183 
184     /**
185      * {@inheritDoc}
186      */
187     public int compare(Class c1, Class c2)  {
188       String s1 = c1 == null ? "null" : c1.getName();
189       String s2 = c2 == null ? "null" : c2.getName();
190 
191       return s1.compareToIgnoreCase(s2);
192     }
193   }
194 
195   /**
196    * Api Wrapper.
197    */
198   private static class Api {
199 
200     /**
201      * The remote interface class.
202      */
203     public Class iface = null;
204 
205     /**
206      * The implementation Class.
207      */
208     public Class impl = null;
209 
210     /**
211      * Remote flag: <code>true</code> if the implementation is from a remote stub.
212      */
213     public boolean isRemote = true;
214 
215     /**
216      * Creates a new instance.
217      *
218      * @param iface the remote interface class
219      * @param impl the implementation class
220      * @param isRemote a boolean set to <code>true</code> if the implementation is
221      *        from a remote stub
222      */
223     public Api(Class iface, Class impl, boolean isRemote) {
224       this.iface = iface;
225       this.impl = impl;
226       this.isRemote = isRemote;
227     }
228   }
229 
230   /**
231    * Adds an Api instance to the list.
232    *
233    * @param interf the remote interface class
234    * @param impl the implementation class
235    * @param isremote a boolean set to <code>true</code> if the implementation is remote,
236    *        <code>false</code> if it is local
237    */
238   private static void addApi(Class interf, Class impl, boolean isremote) {
239     if (interf == null || impl == null) {
240       return;
241     }
242 
243     Api newapi = new Api(interf, impl, isremote);
244     apis.put(impl, newapi);
245 
246     Class oldimpl = (Class)interfaces.get(interf);
247     if (oldimpl != null) {
248       Api oldapi = (Api)apis.get(oldimpl);
249       if (oldapi.isRemote && !isremote) {
250         return;
251       }
252     }
253     interfaces.put(interf, impl);
254   }
255 
256   /**
257    * Gets a list of all APIs.
258    *
259    * @return returns an array of Apis with the list of all registered API wrappers
260    */
261   public static Api[] getAllApis() {
262     return (Api[])apis.values().toArray(new Api[0]);
263   }
264 
265   /**
266    * Gets an instance of an API for the specified remote interface and connection.
267    *
268    * @param iface the interface that the returned instance must implement
269    * @param con the connection to use to create the Api
270    * @return an instance of the specified API implementation
271    * @param <T> Remote Class.
272    *
273    * @throws KernelException <code>CLT011</code> if the connection is null
274    * @throws KernelException <code>CLT015</code> if the interface is null, if is not
275    *                         registered, or if instantiation fails
276    */
277   @SuppressWarnings("unchecked")
278   public static <T extends Remote> T newApi(Class<T> iface, Connection con)
279     throws KernelException  {
280 
281     if (con == null) { // Connection can not be null
282       throw new CKernelException("CLT011");
283     }
284     if (iface == null) {   // Implementation not found for {0} interface
285       throw new CKernelException("CLT015", new String[] { "null" });
286     }
287     Class impl = (Class)interfaces.get(iface);
288     if (impl == null) {
289       throw new CKernelException("CLT015", new String[] { iface.getName() });
290     }
291     T instance;
292     try {
293       Constructor cons = impl.getConstructor(new Class[] { Connection.class });
294       instance = (T)cons.newInstance(new Object[] { con });
295     } catch (Exception ex) {
296       throw new CKernelException("CLT015", new String[] { iface.getName() });
297     }
298     return instance;
299   }
300 
301 }