View Javadoc

1   package org.springframework.roo.bootstrap;
2   
3   import java.io.File;
4   import java.util.ArrayList;
5   import java.util.Arrays;
6   import java.util.HashMap;
7   import java.util.Iterator;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.StringTokenizer;
11  
12  import org.osgi.framework.Bundle;
13  import org.osgi.framework.BundleContext;
14  import org.osgi.framework.BundleException;
15  import org.osgi.framework.Constants;
16  import org.osgi.service.startlevel.StartLevel;
17  
18  /**
19   * Supports {@link Main}.
20   * 
21   * <p>
22   * This class is based on Apache Felix's org.apache.felix.main.AutoProcessor class.
23   *
24   * <p>
25   * For maximum compatibility with Felix, this class has no changes from the Felix original.
26   *
27   * @author Ben Alex
28   *
29   */
30  public class AutoProcessor
31  {
32      /**
33       * The property name used for the bundle directory.
34      **/
35      public static final String AUTO_DEPLOY_DIR_PROPERY = "felix.auto.deploy.dir";
36      /**
37       * The default name used for the bundle directory.
38      **/
39      public static final String AUTO_DEPLOY_DIR_VALUE = "bundle";
40      /**
41       * The property name used to specify auto-deploy actions.
42      **/
43      public static final String AUTO_DEPLOY_ACTION_PROPERY = "felix.auto.deploy.action";
44      /**
45       * The name used for the auto-deploy install action.
46      **/
47      public static final String AUTO_DEPLOY_INSTALL_VALUE = "install";
48      /**
49       * The name used for the auto-deploy start action.
50      **/
51      public static final String AUTO_DEPLOY_START_VALUE = "start";
52      /**
53       * The name used for the auto-deploy update action.
54      **/
55      public static final String AUTO_DEPLOY_UPDATE_VALUE = "update";
56      /**
57       * The name used for the auto-deploy uninstall action.
58      **/
59      public static final String AUTO_DEPLOY_UNINSTALL_VALUE = "uninstall";
60      /**
61       * The property name prefix for the launcher's auto-install property.
62      **/
63      public static final String AUTO_INSTALL_PROP = "felix.auto.install";
64      /**
65       * The property name prefix for the launcher's auto-start property.
66      **/
67      public static final String AUTO_START_PROP = "felix.auto.start";
68  
69      /**
70       * Used to instigate auto-deploy directory process and auto-install/auto-start
71       * configuration property processing during.
72       * @param configMap Map of configuration properties.
73       * @param context The system bundle context.
74      **/
75      public static void process(Map configMap, BundleContext context)
76      {
77          configMap = (configMap == null) ? new HashMap() : configMap;
78          processAutoDeploy(configMap, context);
79          processAutoProperties(configMap, context);
80      }
81  
82      /**
83       * <p>
84       * Processes bundles in the auto-deploy directory, installing and then
85       * starting each one.
86       * </p>
87       */
88      private static void processAutoDeploy(Map configMap, BundleContext context)
89      {
90          // Determine if auto deploy actions to perform.
91          String action = (String) configMap.get(AUTO_DEPLOY_ACTION_PROPERY);
92          action = (action == null) ? "" : action;
93          List actionList = new ArrayList();
94          StringTokenizer st = new StringTokenizer(action, ",");
95          while (st.hasMoreTokens())
96          {
97              String s = st.nextToken().trim().toLowerCase();
98              if (s.equals(AUTO_DEPLOY_INSTALL_VALUE)
99                  || s.equals(AUTO_DEPLOY_START_VALUE)
100                 || s.equals(AUTO_DEPLOY_UPDATE_VALUE)
101                 || s.equals(AUTO_DEPLOY_UNINSTALL_VALUE))
102             {
103                 actionList.add(s);
104             }
105         }
106 
107         // Perform auto-deploy actions.
108         if (actionList.size() > 0)
109         {
110             // Get list of already installed bundles as a map.
111             Map installedBundleMap = new HashMap();
112             Bundle[] bundles = context.getBundles();
113             for (int i = 0; i < bundles.length; i++)
114             {
115                 installedBundleMap.put(bundles[i].getLocation(), bundles[i]);
116             }
117 
118             // Get the auto deploy directory.
119             String autoDir = (String) configMap.get(AUTO_DEPLOY_DIR_PROPERY);
120             autoDir = (autoDir == null) ? AUTO_DEPLOY_DIR_VALUE : autoDir;
121             // Look in the specified bundle directory to create a list
122             // of all JAR files to install.
123             File[] files = new File(autoDir).listFiles();
124             List jarList = new ArrayList();
125             if (files != null)
126             {
127                 Arrays.sort(files);
128                 for (int i = 0; i < files.length; i++)
129                 {
130                     if (files[i].getName().endsWith(".jar"))
131                     {
132                         jarList.add(files[i]);
133                     }
134                 }
135             }
136 
137             // Install bundle JAR files and remember the bundle objects.
138             final List startBundleList = new ArrayList();
139             for (int i = 0; i < jarList.size(); i++)
140             {
141                 // Look up the bundle by location, removing it from
142                 // the map of installed bundles so the remaining bundles
143                 // indicate which bundles may need to be uninstalled.
144                 Bundle b = (Bundle) installedBundleMap.remove(
145                     ((File) jarList.get(i)).toURI().toString());
146                 try
147                 {
148                     // If the bundle is not already installed, then install it
149                     // if the 'install' action is present.
150                     if ((b == null) && actionList.contains(AUTO_DEPLOY_INSTALL_VALUE))
151                     {
152                         b = context.installBundle(
153                             ((File) jarList.get(i)).toURI().toString());
154                     }
155                     // If the bundle is already installed, then update it
156                     // if the 'update' action is present.
157                     else if (actionList.contains(AUTO_DEPLOY_UPDATE_VALUE))
158                     {
159                         b.update();
160                     }
161 
162                     // If we have found and/or successfully installed a bundle,
163                     // then add it to the list of bundles to potentially start.
164                     if (b != null)
165                     {
166                         startBundleList.add(b);
167                     }
168                 }
169                 catch (BundleException ex)
170                 {
171                     System.err.println("Auto-deploy install: "
172                         + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : ""));
173                 }
174             }
175 
176             // Uninstall all bundles not in the auto-deploy directory if
177             // the 'uninstall' action is present.
178             if (actionList.contains(AUTO_DEPLOY_UNINSTALL_VALUE))
179             {
180                 for (Iterator it = installedBundleMap.entrySet().iterator(); it.hasNext(); )
181                 {
182                     Map.Entry entry = (Map.Entry) it.next();
183                     Bundle b = (Bundle) entry.getValue();
184                     if (b.getBundleId() != 0)
185                     {
186                         try
187                         {
188                             b.uninstall();
189                         }
190                         catch (BundleException ex)
191                         {
192                         System.err.println("Auto-deploy uninstall: "
193                             + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : ""));
194                         }
195                     }
196                 }
197             }
198 
199             // Start all installed and/or updated bundles if the 'start'
200             // action is present.
201             if (actionList.contains(AUTO_DEPLOY_START_VALUE))
202             {
203                 for (int i = 0; i < startBundleList.size(); i++)
204                 {
205                     try
206                     {
207                         if (!isFragment((Bundle) startBundleList.get(i)))
208                         {
209                             ((Bundle) startBundleList.get(i)).start();
210                         }
211                     }
212                     catch (BundleException ex)
213                     {
214                         System.err.println("Auto-deploy start: "
215                             + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : ""));
216                     }
217                 }
218             }
219         }
220     }
221 
222     /**
223      * <p>
224      * Processes the auto-install and auto-start properties from the
225      * specified configuration properties.
226      * </p>
227      */
228     private static void processAutoProperties(Map configMap, BundleContext context)
229     {
230         // Retrieve the Start Level service, since it will be needed
231         // to set the start level of the installed bundles.
232         StartLevel sl = (StartLevel) context.getService(
233             context.getServiceReference(org.osgi.service.startlevel.StartLevel.class.getName()));
234 
235         // Retrieve all auto-install and auto-start properties and install
236         // their associated bundles. The auto-install property specifies a
237         // space-delimited list of bundle URLs to be automatically installed
238         // into each new profile, while the auto-start property specifies
239         // bundles to be installed and started. The start level to which the
240         // bundles are assigned is specified by appending a ".n" to the
241         // property name, where "n" is the desired start level for the list
242         // of bundles. If no start level is specified, the default start
243         // level is assumed.
244         for (Iterator i = configMap.keySet().iterator(); i.hasNext(); )
245         {
246             String key = ((String) i.next()).toLowerCase();
247 
248             // Ignore all keys that are not an auto property.
249             if (!key.startsWith(AUTO_INSTALL_PROP) && !key.startsWith(AUTO_START_PROP))
250             {
251                 continue;
252             }
253 
254             // If the auto property does not have a start level,
255             // then assume it is the default bundle start level, otherwise
256             // parse the specified start level.
257             int startLevel = sl.getInitialBundleStartLevel();
258             if (!key.equals(AUTO_INSTALL_PROP) && !key.equals(AUTO_START_PROP))
259             {
260                 try
261                 {
262                     startLevel = Integer.parseInt(key.substring(key.lastIndexOf('.') + 1));
263                 }
264                 catch (NumberFormatException ex)
265                 {
266                     System.err.println("Invalid property: " + key);
267                 }
268             }
269 
270             // Parse and install the bundles associated with the key.
271             StringTokenizer st = new StringTokenizer((String) configMap.get(key), "\" ", true);
272             for (String location = nextLocation(st); location != null; location = nextLocation(st))
273             {
274                 try
275                 {
276                     Bundle b = context.installBundle(location, null);
277                     sl.setBundleStartLevel(b, startLevel);
278                 }
279                 catch (Exception ex)
280                 {
281                     System.err.println("Auto-properties install: " + location + " ("
282                         + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "") + ")");
283 if (ex.getCause() != null)
284     ex.printStackTrace();
285                 }
286             }
287         }
288 
289         // Now loop through the auto-start bundles and start them.
290         for (Iterator i = configMap.keySet().iterator(); i.hasNext(); )
291         {
292             String key = ((String) i.next()).toLowerCase();
293             if (key.startsWith(AUTO_START_PROP))
294             {
295                 StringTokenizer st = new StringTokenizer((String) configMap.get(key), "\" ", true);
296                 for (String location = nextLocation(st); location != null; location = nextLocation(st))
297                 {
298                     // Installing twice just returns the same bundle.
299                     try
300                     {
301                         Bundle b = context.installBundle(location, null);
302                         if (b != null)
303                         {
304                             b.start();
305                         }
306                     }
307                     catch (Exception ex)
308                     {
309                         System.err.println("Auto-properties start: " + location + " ("
310                             + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "") + ")");
311                     }
312                 }
313             }
314         }
315     }
316 
317     private static String nextLocation(StringTokenizer st)
318     {
319         String retVal = null;
320 
321         if (st.countTokens() > 0)
322         {
323             String tokenList = "\" ";
324             StringBuffer tokBuf = new StringBuffer(10);
325             String tok = null;
326             boolean inQuote = false;
327             boolean tokStarted = false;
328             boolean exit = false;
329             while ((st.hasMoreTokens()) && (!exit))
330             {
331                 tok = st.nextToken(tokenList);
332                 if (tok.equals("\""))
333                 {
334                     inQuote = ! inQuote;
335                     if (inQuote)
336                     {
337                         tokenList = "\"";
338                     }
339                     else
340                     {
341                         tokenList = "\" ";
342                     }
343 
344                 }
345                 else if (tok.equals(" "))
346                 {
347                     if (tokStarted)
348                     {
349                         retVal = tokBuf.toString();
350                         tokStarted=false;
351                         tokBuf = new StringBuffer(10);
352                         exit = true;
353                     }
354                 }
355                 else
356                 {
357                     tokStarted = true;
358                     tokBuf.append(tok.trim());
359                 }
360             }
361 
362             // Handle case where end of token stream and
363             // still got data
364             if ((!exit) && (tokStarted))
365             {
366                 retVal = tokBuf.toString();
367             }
368         }
369 
370         return retVal;
371     }
372 
373     private static boolean isFragment(Bundle bundle)
374     {
375         return bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null;
376     }
377 }