KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > idaremedia > antx > flowcontrol > wrap > LocalExecutionBubble


1 /**
2  * $Id: LocalExecutionBubble.java 180 2007-03-15 12:56:38Z ssmc $
3  * Copyright 2004 iDare Media, Inc. All rights reserved.
4  *
5  * Originally written by iDare Media, Inc. for release into the public domain. This
6  * library, source form and binary form, is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License (LGPL) as published
8  * by the Free Software Foundation; either version 2.1 of the License, or (at your option)
9  * any later version.<p>
10  *
11  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU LGPL for more details.<p>
14  *
15  * You should have received a copy of the GNU Lesser General Public License along with this
16  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite
17  * 330, Boston, MA 02111-1307 USA. The GNU LGPL can be found online at
18  * http://www.fsf.org/copyleft/lesser.html<p>
19  *
20  * This product has been influenced by several projects within the open-source community.
21  * The JWare developers wish to acknowledge the open-source community's support. For more
22  * information regarding the open-source products used within JWare, please visit the
23  * JWare website.
24  *----------------------------------------------------------------------------------------*
25  * WEBSITE- http://www.jware.info EMAIL- inquiries@jware.info
26  *----------------------------------------------------------------------------------------*
27  **/

28
29 package com.idaremedia.antx.flowcontrol.wrap;
30
31 import java.util.Hashtable JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.Map JavaDoc;
35
36 import org.apache.tools.ant.Project;
37 import org.apache.tools.ant.PropertyHelper;
38 import org.apache.tools.ant.UnknownElement;
39
40 import com.idaremedia.antx.AntX;
41 import com.idaremedia.antx.AntXFixture;
42 import com.idaremedia.antx.ExportedProperties;
43 import com.idaremedia.antx.FixtureExaminer;
44 import com.idaremedia.antx.Iteration;
45 import com.idaremedia.antx.apis.Requester;
46 import com.idaremedia.antx.apis.Responses;
47 import com.idaremedia.antx.ownhelpers.ProjectDependentSkeleton;
48 import com.idaremedia.antx.ownhelpers.ProjectPropertiesNet;
49
50 /**
51  * Execution helper that tries to capture and restore a project's immediate environment
52  * from a set of local (scoped) changes.
53  *
54  * @since JWare/AntX 0.4
55  * @author ssmc, &copy;2004 <a HREF="http://www.jware.info">iDare&nbsp;Media,&nbsp;Inc.</a>
56  * @version 0.5
57  * @.safety single
58  * @.group impl,helper
59  * @see ProjectPropertiesNet
60  * @see IsolatedTaskSet
61  **/

62
63 public class LocalExecutionBubble extends ProjectDependentSkeleton
64     implements ExecutionBubble
65 {
66     private static final String JavaDoc IAM_= AntX.flow+"LocalBubble";
67
68
69     /**
70      * Initializes a new execution bubble.
71      * @see #addFixtureReset addFixtureReset(&#8230;)
72      **/

73     public LocalExecutionBubble()
74     {
75         super();
76     }
77
78
79     /**
80      * Initializes a new named execution bubble.
81      * @param label this bubble script-facing label (non-null)
82      * @since JWare/AntX 0.5
83      **/

84     public LocalExecutionBubble(String JavaDoc label)
85     {
86         super();
87         m_label = label;
88     }
89
90
91
92     /**
93      * Tells this bubble to examine object reference differences
94      * carefully. If a reference changes (object reference or
95      * content hash) a warning will be issued.
96      * @param careful <i>true</i> to issue warnings for altered
97      * references
98      **/

99     public final void setCarefulObjectChecks(boolean careful)
100     {
101         m_isCareful = careful;
102     }
103
104
105
106     /**
107      * Returns <i>true</i> if this bubble will examine reference
108      * differences carefully and issue warnings for changed
109      * objects. Is <i>false</i> by default.
110      **/

111     public final boolean willBeCareful()
112     {
113         return m_isCareful;
114     }
115
116
117
118     /**
119      * Tells this bubble to update a project property w/ a warning
120      * count if any problems encountered. Must be in "careful"
121      * mode.
122      * @param property name of property to update
123      **/

124     public final void setWarningsUpdateProperty(String JavaDoc property)
125     {
126         m_updateProperty = property;
127     }
128
129
130
131     /**
132      * Tells this bubble to reset a particular fixture component
133      * on leave. Use this method carefully as it impacts information
134      * that spans the entire build-iteration!
135      * @param component the fixture component
136      * @param aspect the aspect to reset (use "all" for everything)
137      **/

138     public void addFixtureReset(String JavaDoc component, String JavaDoc aspect)
139     {
140         if (m_killMethods==null) {
141             m_killMethods = AntXFixture.newMap();
142         }
143         m_killMethods.put(component,aspect);
144     }
145
146
147
148     /**
149      * Adds another inner bubble to this bubble. This
150      * hook allows different tasks to add a little setup and
151      * a little teardown to the mass of standard processing. The
152      * custom bubble is nested "inside" this bubble so its
153      * enter method is called <em>after</em> this bubble has setup
154      * and its leave method is called <em>before</em> this
155      * bubble has reset everything.
156      * @param innerBubble custom setup/teardown helper
157      **/

158     public void setNestedBubble(ExecutionBubble innerBubble)
159     {
160         m_innerLayer = innerBubble;
161     }
162
163
164
165     /**
166      * Assigns a set of filters that can make this bubble
167      * porous. The incoming controls should have been removed from
168      * the project's reference table so it is no longer available
169      * to other bubbles. This method must be called <em>before</em>
170      * this bubble is entered.
171      * @param controls the filter controls
172      * @since JWare/AntX 0.5
173      **/

174     public final void setFilterControls(Locals controls)
175     {
176         AntX.verify_(m_propertiesNet==null,IAM_,"setCtrls- not active");
177         m_controls = controls;
178     }
179
180
181     /**
182      * Returns <i>true</i> if this bubble has already been
183      * assigned a set of local filters.
184      * @see #setFilterControls setFilterControls()
185      * @since JWare/AntX 0.5
186      **/

187     public final boolean isFiltered()
188     {
189         return m_controls!=null;
190     }
191
192
193
194     /**
195      * Tells this bubble whether default fixture exclusions should
196      * be ignored. For backward compatibility, fixture exclusions
197      * are ignored unless explicitly turned on.
198      * @param letEmPassThru <i>true</i> to let exclusion thru
199      * @since JWare/AntX 0.5
200      **/

201     public void setFixtureExcludes(boolean letEmPassThru)
202     {
203         m_defaultExcludes = letEmPassThru;
204     }
205
206
207
208     /**
209      * Captures the calling task's surrounding project state
210      * for later restoration. Must be matched with a call to
211      * {@linkplain #leave leave}.
212      * @param task the controlling task (non-null)
213      **/

214     public boolean enter(Requester task)
215     {
216         Project project = task.getProject();
217         AntX.verify_(project!=null,IAM_,"enter- inited task");
218
219         setProject(project);
220         
221         captureEnvironment(project, task);
222         applyLocals();
223
224         boolean ok=true;
225         if (m_innerLayer!=null) {
226             ok = m_innerLayer.enter(task);
227             if (!ok) {
228                 restoreEnvironment(task);
229             }
230         }
231         return ok;
232     }
233
234
235
236     /**
237      * Restores the project's state as best it can to its state before the
238      * task was executed. Will affect project properties and references
239      * mostly. Should be done <em>once</em> for each call to
240      * {@linkplain #enter enter}.
241      * @param task the controlling task (non-null)
242      * @throws org.apache.tools.ant.BuildException if unable to restore
243      * project's PropertyHelper.
244      **/

245     public boolean leave(Requester task)
246     {
247         Project project= task.getProject();
248         AntX.verify_(getProject()==project,IAM_,
249                      "leave- project same as enter()");
250
251         boolean ok= m_innerLayer==null;
252         try {
253             if (m_innerLayer!=null) {
254                ok = m_innerLayer.leave(task);
255             }
256         } finally {
257             restoreEnvironment(task);
258             resetIterationVarStacks(task);
259         }
260         return ok;
261     }
262
263
264
265     /**
266      * Captures the standard project fixture bits only. The order
267      * of capture is important with the properties net being cast
268      * last.
269      * @param project target project (non-null)
270      * @param task call controller (non-null)
271      * @since JWare/AntX 0.5
272      **/

273     private void captureEnvironment(Project project, Requester task)
274     {
275         captureRefList(project);
276         captureVarList(project);
277         m_propertiesNet = new ProjectPropertiesNet(m_label,project);
278     }
279
280
281
282     /**
283      * Restores the standard project fixture bits only. Iteration
284      * fixture overlays are not touched.
285      * @param task the controlling task (non-null)
286      * @since JWare/AntX 0.5
287      **/

288     private void restoreEnvironment(Requester task)
289     {
290         m_propertiesNet.uninstall(new Responses.LogUsing(task));
291         passthruPropList(task);
292         restoreVarList(task);
293         restoreRefList(task);
294     }
295
296
297
298     /**
299      * Apply local (but isolated) fixture instructions.
300      **/

301     private void applyLocals()
302     {
303         if (m_controls!=null) {
304             applyLocalVars();
305             applyLocalProperties();
306         }
307     }
308
309
310
311
312     /**
313      * Applies local property (un)assignment instructions. These
314      * changes are visible to all tasks within bubble from same
315      * iteration/VM.
316      * @since JWare/AntX 0.5
317      **/

318     private void applyLocalProperties()
319     {
320         List JavaDoc from = m_controls.getProperties();
321         if (from!=null) {
322             synchronized(from) {
323                 Hashtable JavaDoc ht = new Hashtable JavaDoc(from.size(),0.9f);
324                 for (int i=0,N=from.size();i<N;i++) {
325                     ConditionalLocal local = (ConditionalLocal)from.get(i);
326                     if (local.isEnabled()) {
327                         //NB: Use the empty string as a proxy for unset!
328
String JavaDoc value = local.getValue();
329                         if (value!=null) {
330                             ht.put(local.getName(),value);
331                         }
332                         else {
333                             Boolean JavaDoc f = m_controls.getInheritFlag(local);
334                             if (f==null || f==Boolean.FALSE) {
335                                 ht.put(local.getName(),"");
336                             } else {//NB: Properties must be explicitly inherited!
337
value = getProject().getProperty(local.getName());
338                                 if (value==null) {
339                                     value = "";
340                                 }
341                                 ht.put(local.getName(),value);
342                             }
343                         }
344                     }//enabled
345
}//foreach
346
m_propertiesNet.seedProperties(ht,false,false);
347             }//lock
348
}//!=null
349
}
350
351
352
353     /**
354      * Apply any passed through property settings to our enclosing
355      * context. Must be called <em>after</em> properties net removed.
356      * Pre-existing script properties are not altered!
357      * @param task controlling task (non-null)
358      * @since JWare/AntX 0.5
359      **/

360     private void passthruPropList(Requester task)
361     {
362         if (m_controls!=null) {
363             boolean allowThru = m_controls.isBlocking();
364             Map JavaDoc edits = m_propertiesNet.getFinalProperties(true,allowThru);
365             if (!edits.isEmpty()) {
366                 PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject());
367                 Iterator JavaDoc itr= edits.entrySet().iterator();
368                 while (itr.hasNext()) {
369                     Map.Entry JavaDoc e = (Map.Entry JavaDoc)itr.next();
370                     ph.setNewProperty(null,(String JavaDoc)e.getKey(),
371                         (String JavaDoc)e.getValue());//*QUIETLY*
372
}
373             }
374         }
375     }
376
377
378
379     /**
380      * Remembers the project's current set of references. If we're
381      * being careful we remember some hash values for comparison
382      * on 'leave'.
383      **/

384     private void captureRefList(Project P)
385     {
386         Iterator JavaDoc itr;
387         Map JavaDoc current = P.getReferences();
388         synchronized(current) {
389             if (willBeCareful()) {
390                 Map JavaDoc mp= AntXFixture.newMap();
391                 itr= current.entrySet().iterator();
392                 while(itr.hasNext()) {
393                     Map.Entry JavaDoc mE= (Map.Entry JavaDoc)itr.next();
394                     mp.put(mE.getKey(), new RefInfo(mE));
395                 }
396                 if (m_defaultExcludes) {
397                     List JavaDoc special = Iteration.defaultFixtureExcludes()
398                         .copyOfReferencesIfAny();
399                     if (special!=null) {
400                        itr= special.iterator();
401                        while (itr.hasNext()) {
402                            String JavaDoc refid = itr.next().toString();
403                            if (!mp.containsKey(refid)) {
404                                mp.put(refid, new RefInfo(refid));
405                            }
406                        }
407                     }
408                 }
409                 m_refInfo= mp;
410             } else {
411                 List JavaDoc l= AntXFixture.newList();
412                 itr= current.keySet().iterator();
413                 while (itr.hasNext()) {
414                     l.add(itr.next());
415                 }
416                 if (m_defaultExcludes) {
417                     Iteration.defaultFixtureExcludes().addReferencesTo(l);
418                 }
419                 m_refInfo= l;
420             }
421         }
422     }
423
424
425
426     /**
427      * Restores the project's set of references as best can.
428      * Will <em>remove</em> references that weren't there but will
429      * <em>not</em> put back references because we cannot guarantee
430      * that the underlying objects are still valid. Also with Ant 1.6x
431      * UnknownElement are dynamically flipped to their real objects
432      * so we cannot determine if a change is part of Ant's function.
433      * If we're being careful we can check for altered object references
434      * (and contents if reference implementation supports this).
435      * @param task controlling task (non-null)
436      **/

437     private void restoreRefList(Requester task)
438     {
439         Project P= task.getProject();
440         Map JavaDoc current = P.getReferences();
441         synchronized(current) {
442             if (willBeCareful()) {
443                 Map JavaDoc mp = (Map JavaDoc)m_refInfo;
444                 current.keySet().retainAll(mp.keySet());
445                 RefInfo ri= new RefInfo();
446                 int whoopsies=0;
447                 for (Iterator JavaDoc itr=mp.values().iterator();itr.hasNext();) {
448                     RefInfo ri0 = (RefInfo)itr.next();
449                     if (!current.containsKey(ri0.key)) {
450                         String JavaDoc warning = AntX.uistrs().get
451                             ("fixture.buble.oldref.misin",ri0.key);
452                         task.log(warning,Project.MSG_WARN);
453                         whoopsies++;
454                     } else {
455                         ri.set(ri0.key,P);
456                         String JavaDoc warning = ri0.compare(ri);
457                         if (warning!=null) {
458                             task.log(warning,Project.MSG_WARN);
459                             whoopsies++;
460                         }
461                     }
462                 }
463                 mp.clear();//gc
464

465                 if (m_updateProperty!=null && whoopsies>0) {
466                     String JavaDoc updateProperty = m_updateProperty+"-refs";
467                     FixtureExaminer.checkIfProperty(P,task,updateProperty,true);
468                     P.setNewProperty(updateProperty,String.valueOf(whoopsies));
469                 }
470             }
471             else {
472                 List JavaDoc l= (List JavaDoc)m_refInfo;
473                 current.keySet().retainAll(l);
474                 l.clear();//gc
475
}
476         }
477         m_refInfo=null;
478     }
479
480
481
482
483     /**
484      * Remembers the thread's current set of variables iff there
485      * are local variable controls to be applied on exit.
486      * @since JWare/AntX 0.5
487      **/

488     private void captureVarList(Project P)
489     {
490         if (m_controls!=null && m_controls.hasVariables()) {
491             if (!m_controls.isBlocking()) {
492                 m_varInfo = Iteration.exportableProperties().copyOfPropertyNames();
493             }
494         }
495     }
496  
497  
498  
499     /**
500      * Restores the threads set of variables as defined by the
501      * local variables controls. Note that unlike references,
502      * AntX variable contents are never tested against any base
503      * values.
504      * @param task controlling task (non-null)
505      * @since JWare/AntX 0.5
506      **/

507     private void restoreVarList(Requester task)
508     {
509         if (m_controls!=null && m_controls.hasVariables()) {
510             List JavaDoc vars = m_controls.getVariableNames();
511             if (m_controls.isBlocking()) {
512                 ExportedProperties.delete(vars);
513             } else {
514                 List JavaDoc l = (List JavaDoc)m_varInfo;//originally-there
515
l.addAll(vars);//+explicitly-saved
516
ExportedProperties.retain(l);
517                 l.clear();//gc
518
}
519         }
520     }
521
522
523
524     /**
525      * Applies local variable (un)assignment instructions. These
526      * changes are visible to all tasks within bubble from same
527      * iteration/VM.
528      * @since JWare/AntX 0.5
529      **/

530     private void applyLocalVars()
531     {
532         List JavaDoc from = m_controls.getVariables();
533         if (from!=null) {
534             synchronized(from) {
535                 for (int i=0,N=from.size();i<N;i++) {
536                     ConditionalLocal local = (ConditionalLocal)from.get(i);
537                     if (local.isEnabled()) {
538                         String JavaDoc value = local.getValue();
539                         if (value!=null) {
540                             ExportedProperties.set(local.getName(),value);
541                         }
542                         else if (m_controls.getInheritFlag(local)==Boolean.FALSE) {
543                             ExportedProperties.unset(local.getName());
544                         }
545                     }//enabled
546
}//foreach
547
}//lock
548
}//!=null
549
}
550
551
552
553     /**
554      * Applies this bubble's fixture kill methods.
555      * @param task the controlling task (non-null)
556      **/

557     private void resetIterationVarStacks(Requester task)
558     {
559         if (m_killMethods!=null) {
560             Project P= task.getProject();
561             Iterator JavaDoc itr= m_killMethods.entrySet().iterator();
562             Responses.LogAndRemember logr = new Responses.LogAndRemember(task);
563
564             while (itr.hasNext()) {
565                 Map.Entry JavaDoc mE= (Map.Entry JavaDoc)itr.next();
566                 AntXFixture.reset((String JavaDoc)mE.getKey(),(String JavaDoc)mE.getValue(),logr);
567                 if (logr.hadProblem) {
568                     if (m_updateProperty!=null) {
569                         String JavaDoc updateProperty = m_updateProperty+"-"+mE.getKey();
570                         FixtureExaminer.checkIfProperty(P,task,updateProperty,true);
571                         P.setNewProperty(updateProperty,logr.what);
572                     }
573                     logr.reset();
574                 }
575             }
576         }
577     }
578
579
580
581     private Locals m_controls;
582     private boolean m_isCareful;//NB:=> do not check hashvalues!
583
private boolean m_defaultExcludes = Iteration.defaultdefaults().isFixtureExcludesEnabled();
584     private ProjectPropertiesNet m_propertiesNet;
585     private Object JavaDoc m_refInfo;
586     private Map JavaDoc m_killMethods;
587     private String JavaDoc m_updateProperty;
588     private ExecutionBubble m_innerLayer;
589     private Object JavaDoc m_varInfo;//NB: only touched if have local controls
590
private String JavaDoc m_label = "isolate";
591
592
593
594     /**
595      * Keeps track of extra object reference information for closer
596      * examination on restore.
597      * @since JWare/AntX 0.4
598      * @author ssmc, &copy;2004 <a HREF="http://www.jware.info">iDare&nbsp;Media,&nbsp;Inc.</a>
599      * @version 0.5
600      * @.safety single
601      * @.group impl,helper
602      **/

603     private static class RefInfo
604     {
605         RefInfo() {
606         }
607         RefInfo(Map.Entry JavaDoc e) {
608             set(e.getKey().toString(),e.getValue());
609         }
610         RefInfo(String JavaDoc refid) {
611             set(refid,null);
612         }
613         void set(String JavaDoc key, Object JavaDoc value) {
614             this.key = key;
615             identityHash = System.identityHashCode(value);
616             contentHash = identityHash;
617             this.unknown = (value instanceof UnknownElement);
618             if (value!=null && !this.unknown) {
619                 contentHash = value.hashCode();
620             }
621         }
622         void set(String JavaDoc key, Project P) {
623             set(key, FixtureExaminer.trueReference(P,key)/*!!!*NOT* P.getReference()*/);
624         }
625         String JavaDoc compare(RefInfo tmp) {
626             if (!unknown) {
627                 if (tmp.identityHash!=identityHash) {
628                     return Iteration.uistrs().get("fixture.buble.oldref.switched",key);
629                 }
630                 if (tmp.contentHash!=contentHash) {
631                     return Iteration.uistrs().get("fixture.buble.oldref.chged",key);
632                 }
633             }
634             return null;
635         }
636         String JavaDoc key;
637         int identityHash;
638         int contentHash;
639         boolean unknown;//true means Ant can resolve behind our backs
640
}
641 }
642
643 /* end-of-LocalExecutionBubble.java */
644
Popular Tags