001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.felix.framework;
020
021 import java.util.ArrayList;
022 import java.util.Collections;
023 import java.util.HashMap;
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.Map;
027 import org.apache.felix.framework.searchpolicy.ResolveException;
028 import org.apache.felix.framework.searchpolicy.Resolver;
029 import org.apache.felix.framework.util.Util;
030 import org.apache.felix.framework.util.VersionRange;
031 import org.apache.felix.framework.util.manifestparser.R4Attribute;
032 import org.apache.felix.framework.util.manifestparser.R4Directive;
033 import org.apache.felix.framework.util.manifestparser.Requirement;
034 import org.apache.felix.moduleloader.ICapability;
035 import org.apache.felix.moduleloader.IModule;
036 import org.apache.felix.moduleloader.IRequirement;
037 import org.apache.felix.moduleloader.IWire;
038 import org.osgi.framework.BundlePermission;
039 import org.osgi.framework.Constants;
040 import org.osgi.framework.PackagePermission;
041 import org.osgi.framework.Version;
042
043 public class FelixResolverState implements Resolver.ResolverState
044 {
045 private final Logger m_logger;
046 // List of all modules.
047 private final List m_moduleList = new ArrayList();
048 // Map of fragment symbolic names to list of fragment modules sorted by version.
049 private final Map m_fragmentMap = new HashMap();
050 // Maps a package name to a list of exporting capabilities.
051 private final Map m_unresolvedPkgIndex = new HashMap();
052 // Maps a package name to a list of exporting capabilities.
053 private final Map m_resolvedPkgIndex = new HashMap();
054 // Maps a module to a list of capabilities.
055 private final Map m_resolvedCapMap = new HashMap();
056
057 public FelixResolverState(Logger logger)
058 {
059 m_logger = logger;
060 }
061
062 public synchronized void addModule(IModule module)
063 {
064 if (Util.isFragment(module))
065 {
066 addFragment(module);
067 }
068 else
069 {
070 addHost(module);
071 }
072
073 //System.out.println("UNRESOLVED PACKAGES:");
074 //dumpPackageIndex(m_unresolvedPkgIndex);
075 //System.out.println("RESOLVED PACKAGES:");
076 //dumpPackageIndex(m_resolvedPkgIndex);
077 }
078
079 public synchronized void removeModule(IModule module)
080 {
081 if (Util.isFragment(module))
082 {
083 removeFragment(module);
084 }
085 else
086 {
087 removeHost(module);
088 }
089 }
090
091 private void addFragment(IModule fragment)
092 {
093 // TODO: FRAGMENT - This should check to make sure that the host allows fragments.
094 IModule bestFragment = indexFragment(m_fragmentMap, fragment);
095
096 // If the newly added fragment is the highest version for
097 // its given symbolic name, then try to merge it to any
098 // matching unresolved hosts and remove the previous highest
099 // version of the fragment.
100 if (bestFragment == fragment)
101 {
102
103 // If we have any matching hosts, then merge the new fragment while
104 // removing any older version of the new fragment. Also remove host's
105 // existing capabilities from the package index and reindex its new
106 // ones after attaching the fragment.
107 List matchingHosts = getMatchingHosts(fragment);
108 for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
109 {
110 IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
111
112 // Get the fragments currently attached to the host so we
113 // can remove the older version of the current fragment, if any.
114 IModule[] fragments = ((ModuleImpl) host).getFragments();
115 List fragmentList = new ArrayList();
116 for (int fragIdx = 0;
117 (fragments != null) && (fragIdx < fragments.length);
118 fragIdx++)
119 {
120 if (!fragments[fragIdx].getSymbolicName().equals(
121 bestFragment.getSymbolicName()))
122 {
123 fragmentList.add(fragments[fragIdx]);
124 }
125 }
126
127 // Now add the new fragment in bundle ID order.
128 int index = -1;
129 for (int listIdx = 0;
130 (index < 0) && (listIdx < fragmentList.size());
131 listIdx++)
132 {
133 IModule f = (IModule) fragmentList.get(listIdx);
134 if (bestFragment.getBundle().getBundleId()
135 < f.getBundle().getBundleId())
136 {
137 index = listIdx;
138 }
139 }
140 fragmentList.add(
141 (index < 0) ? fragmentList.size() : index, bestFragment);
142
143 // Remove host's existing exported packages from index.
144 ICapability[] caps = host.getCapabilities();
145 for (int i = 0; (caps != null) && (i < caps.length); i++)
146 {
147 if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
148 {
149 // Get package name.
150 String pkgName = (String)
151 caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
152 // Remove from "unresolved" package map.
153 List capList = (List) m_unresolvedPkgIndex.get(pkgName);
154 if (capList != null)
155 {
156 capList.remove(caps[i]);
157 }
158 }
159 }
160
161 // Check if fragment conflicts with existing metadata.
162 checkForConflicts(host, fragmentList);
163
164 // Attach the fragments to the host.
165 fragments = (fragmentList.size() == 0)
166 ? null
167 : (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
168 try
169 {
170 ((ModuleImpl) host).attachFragments(fragments);
171 }
172 catch (Exception ex)
173 {
174 // Try to clean up by removing all fragments.
175 try
176 {
177 ((ModuleImpl) host).attachFragments(null);
178 }
179 catch (Exception ex2)
180 {
181 }
182 m_logger.log(Logger.LOG_ERROR,
183 "Serious error attaching fragments.", ex);
184 }
185
186 // Reindex the host's exported packages.
187 caps = host.getCapabilities();
188 for (int i = 0; (caps != null) && (i < caps.length); i++)
189 {
190 if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
191 {
192 indexPackageCapability(m_unresolvedPkgIndex, caps[i]);
193 }
194 }
195 }
196 }
197 }
198
199 private void removeFragment(IModule fragment)
200 {
201 // Get fragment list, which may be null for system bundle fragments.
202 List fragList = (List) m_fragmentMap.get(fragment.getSymbolicName());
203 if (fragList != null)
204 {
205 // Remove from fragment map.
206 fragList.remove(fragment);
207 if (fragList.size() == 0)
208 {
209 m_fragmentMap.remove(fragment.getSymbolicName());
210 }
211
212 // If we have any matching hosts, then remove fragment while
213 // removing any older version of the new fragment. Also remove host's
214 // existing capabilities from the package index and reindex its new
215 // ones after attaching the fragment.
216 List matchingHosts = getMatchingHosts(fragment);
217 for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
218 {
219 IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
220
221 // Check to see if the removed fragment was actually merged with
222 // the host, since it might not be if it wasn't the highest version.
223 // If it was, recalculate the fragments for the host.
224 IModule[] fragments = ((ModuleImpl) host).getFragments();
225 for (int fragIdx = 0;
226 (fragments != null) && (fragIdx < fragments.length);
227 fragIdx++)
228 {
229 if (!fragments[fragIdx].equals(fragment))
230 {
231 List fragmentList = getMatchingFragments(host);
232
233 // Remove host's existing exported packages from index.
234 ICapability[] caps = host.getCapabilities();
235 for (int i = 0; (caps != null) && (i < caps.length); i++)
236 {
237 if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
238 {
239 // Get package name.
240 String pkgName = (String)
241 caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
242 // Remove from "unresolved" package map.
243 List capList = (List) m_unresolvedPkgIndex.get(pkgName);
244 if (capList != null)
245 {
246 capList.remove(caps[i]);
247 }
248 }
249 }
250
251 // Check if fragment conflicts with existing metadata.
252 checkForConflicts(host, fragmentList);
253
254 // Attach the fragments to the host.
255 fragments = (fragmentList.size() == 0)
256 ? null
257 : (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
258 try
259 {
260 ((ModuleImpl) host).attachFragments(fragments);
261 }
262 catch (Exception ex)
263 {
264 // Try to clean up by removing all fragments.
265 try
266 {
267 ((ModuleImpl) host).attachFragments(null);
268 }
269 catch (Exception ex2)
270 {
271 }
272 m_logger.log(Logger.LOG_ERROR,
273 "Serious error attaching fragments.", ex);
274 }
275
276 // Reindex the host's exported packages.
277 caps = host.getCapabilities();
278 for (int i = 0; (caps != null) && (i < caps.length); i++)
279 {
280 if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
281 {
282 indexPackageCapability(m_unresolvedPkgIndex, caps[i]);
283 }
284 }
285 }
286 }
287 }
288 }
289 }
290
291 public void unmergeFragment(IModule module)
292 {
293 if (!Util.isFragment(module))
294 {
295 return;
296 }
297
298 // Get fragment list, which may be null for system bundle fragments.
299 List fragList = (List) m_fragmentMap.get(module.getSymbolicName());
300 if (fragList != null)
301 {
302 // Remove from fragment map.
303 fragList.remove(module);
304 if (fragList.size() == 0)
305 {
306 m_fragmentMap.remove(module.getSymbolicName());
307 }
308
309 // If we have any matching hosts, then remove fragment while
310 // removing any older version of the new fragment. Also remove host's
311 // existing capabilities from the package index and reindex its new
312 // ones after attaching the fragment.
313 List matchingHosts = getMatchingHosts(module);
314 for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
315 {
316 IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
317 // Find any unresolved hosts into which the fragment is merged
318 // and unmerge it.
319 IModule[] fragments = ((ModuleImpl) host).getFragments();
320 for (int fragIdx = 0;
321 !host.isResolved() && (fragments != null) && (fragIdx < fragments.length);
322 fragIdx++)
323 {
324 if (!fragments[fragIdx].equals(module))
325 {
326 List fragmentList = getMatchingFragments(host);
327
328 // Remove host's existing exported packages from index.
329 ICapability[] caps = host.getCapabilities();
330 for (int i = 0; (caps != null) && (i < caps.length); i++)
331 {
332 if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
333 {
334 // Get package name.
335 String pkgName = (String)
336 caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
337 // Remove from "unresolved" package map.
338 List capList = (List) m_unresolvedPkgIndex.get(pkgName);
339 if (capList != null)
340 {
341 capList.remove(caps[i]);
342 }
343 }
344 }
345
346 // Check if fragment conflicts with existing metadata.
347 checkForConflicts(host, fragmentList);
348
349 // Attach the fragments to the host.
350 fragments = (fragmentList.size() == 0)
351 ? null
352 : (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
353 try
354 {
355 ((ModuleImpl) host).attachFragments(fragments);
356 }
357 catch (Exception ex)
358 {
359 // Try to clean up by removing all fragments.
360 try
361 {
362 ((ModuleImpl) host).attachFragments(null);
363 }
364 catch (Exception ex2)
365 {
366 }
367 m_logger.log(Logger.LOG_ERROR,
368 "Serious error attaching fragments.", ex);
369 }
370
371 // Reindex the host's exported packages.
372 caps = host.getCapabilities();
373 for (int i = 0; (caps != null) && (i < caps.length); i++)
374 {
375 if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
376 {
377 indexPackageCapability(m_unresolvedPkgIndex, caps[i]);
378 }
379 }
380 }
381 }
382 }
383 }
384 }
385
386 private List getMatchingHosts(IModule fragment)
387 {
388 // Find the fragment's host requirement.
389 IRequirement hostReq = getFragmentHostRequirement(fragment);
390
391 // Create a list of all matching hosts for this fragment.
392 List matchingHosts = new ArrayList();
393 SecurityManager sm = System.getSecurityManager();
394 if ((sm != null) && (fragment.getSymbolicName() != null))
395 {
396 if (!((BundleProtectionDomain) fragment.getSecurityContext()).impliesDirect(new BundlePermission(
397 fragment.getSymbolicName(), BundlePermission.FRAGMENT)))
398 {
399 return matchingHosts;
400 }
401 }
402 for (int hostIdx = 0; (hostReq != null) && (hostIdx < m_moduleList.size()); hostIdx++)
403 {
404 IModule host = (IModule) m_moduleList.get(hostIdx);
405 // Only look at unresolved hosts, since we don't support
406 // dynamic attachment of fragments.
407 if (host.isResolved()
408 || ((BundleImpl) host.getBundle()).isStale()
409 || ((BundleImpl) host.getBundle()).isRemovalPending())
410 {
411 continue;
412 }
413
414 // Find the host capability for the current host.
415 ICapability hostCap = Util.getSatisfyingCapability(host, hostReq);
416
417 // If there is no host capability in the current module,
418 // then just ignore it.
419 if (hostCap == null)
420 {
421 continue;
422 }
423
424 if ((sm != null) && (host.getSymbolicName() != null))
425 {
426 if (!((BundleProtectionDomain) host.getSecurityContext()).impliesDirect(new BundlePermission(host.getSymbolicName(),
427 BundlePermission.HOST)))
428 {
429 continue;
430 }
431 }
432
433 matchingHosts.add(hostCap);
434 }
435
436 return matchingHosts;
437 }
438
439 private void checkForConflicts(IModule host, List fragmentList)
440 {
441 if ((fragmentList == null) || (fragmentList.size() == 0))
442 {
443 return;
444 }
445
446 // Verify the fragments do not have conflicting imports.
447 // For now, just check for duplicate imports, but in the
448 // future we might want to make this more fine grained.
449 // First get the host's imported packages.
450 final int MODULE_IDX = 0, REQ_IDX = 1;
451 Map ipMerged = new HashMap();
452 Map rbMerged = new HashMap();
453 IRequirement[] reqs = host.getRequirements();
454 for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
455 {
456 if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
457 {
458 ipMerged.put(
459 ((Requirement) reqs[reqIdx]).getTargetName(),
460 new Object[] { host, reqs[reqIdx] });
461 }
462 else if (reqs[reqIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
463 {
464 rbMerged.put(
465 ((Requirement) reqs[reqIdx]).getTargetName(),
466 new Object[] { host, reqs[reqIdx] });
467 }
468 }
469 // Loop through each fragment verifying it does not conflict.
470 // Add its package and bundle dependencies if they do not
471 // conflict or remove the fragment if it does conflict.
472 for (Iterator it = fragmentList.iterator(); it.hasNext(); )
473 {
474 IModule fragment = (IModule) it.next();
475 reqs = fragment.getRequirements();
476 Map ipFragment = new HashMap();
477 Map rbFragment = new HashMap();
478 for (int reqIdx = 0;
479 (reqs != null) && (reqIdx < reqs.length);
480 reqIdx++)
481 {
482 if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
483 || reqs[reqIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
484 {
485 String targetName = ((Requirement) reqs[reqIdx]).getTargetName();
486 Map mergedReqMap =
487 (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
488 ? ipMerged : rbMerged;
489 Map fragmentReqMap =
490 (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
491 ? ipFragment : rbFragment;
492 Object[] existing = (Object[]) mergedReqMap.get(targetName);
493 if (existing == null)
494 {
495 fragmentReqMap.put(targetName, new Object[] { fragment, reqs[reqIdx] });
496 }
497 else if (isRequirementConflicting(
498 (Requirement) existing[REQ_IDX], (Requirement) reqs[reqIdx]))
499 {
500 ipFragment.clear();
501 rbFragment.clear();
502 it.remove();
503 m_logger.log(
504 Logger.LOG_DEBUG,
505 "Excluding fragment " + fragment.getSymbolicName()
506 + " from " + host.getSymbolicName()
507 + " due to conflict with "
508 + (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
509 ? "imported package " : "required bundle ")
510 + targetName + " from "
511 + ((IModule) existing[MODULE_IDX]).getSymbolicName());
512 // No need to finish processing current fragment.
513 break;
514 }
515 else
516 {
517 // If there is an overlapping requirement for the existing
518 // target, then try to calculate the intersecting requirement
519 // and set the existing requirement to that instead. This
520 // makes it so version ranges do not have to be exact, just
521 // overlapping.
522 Requirement intersection = calculateVersionIntersection(
523 (Requirement) existing[REQ_IDX], (Requirement) reqs[reqIdx]);
524 if (intersection != existing[REQ_IDX])
525 {
526 existing[REQ_IDX] = intersection;
527 }
528 }
529 }
530 }
531
532 // Merge non-conflicting requirements into overall set
533 // of requirements and continue checking for conflicts
534 // with the next fragment.
535 for (Iterator it2 = ipFragment.entrySet().iterator(); it2.hasNext(); )
536 {
537 Map.Entry entry = (Map.Entry) it2.next();
538 ipMerged.put(entry.getKey(), entry.getValue());
539 }
540 for (Iterator it2 = rbFragment.entrySet().iterator(); it2.hasNext(); )
541 {
542 Map.Entry entry = (Map.Entry) it2.next();
543 rbMerged.put(entry.getKey(), entry.getValue());
544 }
545 }
546 }
547
548 private boolean isRequirementConflicting(
549 Requirement existing, Requirement additional)
550 {
551 // If the namespace is not the same, then they do NOT conflict.
552 if (!existing.getNamespace().equals(additional.getNamespace()))
553 {
554 return false;
555 }
556 // If the target name is not the same, then they do NOT conflict.
557 if (!existing.getTargetName().equals(additional.getTargetName()))
558 {
559 return false;
560 }
561 // If the existing version range floor is greater than the additional
562 // version range's floor, then they are inconflict since we cannot
563 // widen the constraint.
564 if (!existing.getTargetVersionRange().intersects(
565 additional.getTargetVersionRange()))
566 {
567 return true;
568 }
569 // If optionality is not the same, then they conflict, unless
570 // the existing requirement is not optional, then it doesn't matter
571 // what subsequent requirements are since non-optional is stronger
572 // than optional.
573 if (existing.isOptional() && !additional.isOptional())
574 {
575 return true;
576 }
577 // Verify directives are the same.
578 final R4Directive[] exDirs = (existing.getDirectives() == null)
579 ? new R4Directive[0] : existing.getDirectives();
580 final R4Directive[] addDirs = (additional.getDirectives() == null)
581 ? new R4Directive[0] : additional.getDirectives();
582 // Put attributes in a map, since ordering is arbitrary.
583 final Map exDirMap = new HashMap();
584 for (int i = 0; i < exDirs.length; i++)
585 {
586 exDirMap.put(exDirs[i].getName(), exDirs[i]);
587 }
588 // If attribute values do not match, then they conflict.
589 for (int i = 0; i < addDirs.length; i++)
590 {
591 // Ignore resolution directive, since we've already tested it above.
592 if (!addDirs[i].getName().equals(Constants.RESOLUTION_DIRECTIVE))
593 {
594 final R4Directive exDir = (R4Directive) exDirMap.get(addDirs[i].getName());
595 if ((exDir == null) ||
596 !exDir.getValue().equals(addDirs[i].getValue()))
597 {
598 return true;
599 }
600 }
601 }
602 // Verify attributes are the same.
603 final R4Attribute[] exAttrs = (existing.getAttributes() == null)
604 ? new R4Attribute[0] : existing.getAttributes();
605 final R4Attribute[] addAttrs = (additional.getAttributes() == null)
606 ? new R4Attribute[0] : additional.getAttributes();
607 // Put attributes in a map, since ordering is arbitrary.
608 final Map exAttrMap = new HashMap();
609 for (int i = 0; i < exAttrs.length; i++)
610 {
611 exAttrMap.put(exAttrs[i].getName(), exAttrs[i]);
612 }
613 // If attribute values do not match, then they conflict.
614 for (int i = 0; i < addAttrs.length; i++)
615 {
616 // Ignore version property, since we've already tested it above.
617 if (!(additional.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
618 && addAttrs[i].getName().equals(ICapability.VERSION_PROPERTY))
619 && !(additional.getNamespace().equals(ICapability.MODULE_NAMESPACE)
620 && addAttrs[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE)))
621 {
622 final R4Attribute exAttr = (R4Attribute) exAttrMap.get(addAttrs[i].getName());
623 if ((exAttr == null) ||
624 !exAttr.getValue().equals(addAttrs[i].getValue()) ||
625 (exAttr.isMandatory() != addAttrs[i].isMandatory()))
626 {
627 return true;
628 }
629 }
630 }
631 // They do no conflict.
632 return false;
633 }
634
635 static Requirement calculateVersionIntersection(
636 Requirement existing, Requirement additional)
637 {
638 Requirement intersection = existing;
639 int existVersionIdx = -1, addVersionIdx = -1;
640
641 // Find the existing version attribute.
642 for (int i = 0; (existVersionIdx < 0) && (i < existing.getAttributes().length); i++)
643 {
644 if ((existing.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
645 && existing.getAttributes()[i].getName().equals(ICapability.VERSION_PROPERTY))
646 || (existing.getNamespace().equals(ICapability.MODULE_NAMESPACE)
647 && existing.getAttributes()[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE)))
648 {
649 existVersionIdx = i;
650 }
651 }
652
653 // Find the additional version attribute.
654 for (int i = 0; (addVersionIdx < 0) && (i < additional.getAttributes().length); i++)
655 {
656 if ((additional.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
657 && additional.getAttributes()[i].getName().equals(ICapability.VERSION_PROPERTY))
658 || (additional.getNamespace().equals(ICapability.MODULE_NAMESPACE)
659 && additional.getAttributes()[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE)))
660 {
661 addVersionIdx = i;
662 }
663 }
664
665 // Use the additional requirement's version range if it
666 // has one and the existing requirement does not.
667 if ((existVersionIdx == -1) && (addVersionIdx != -1))
668 {
669 intersection = additional;
670 }
671 // If both requirements have version ranges, then create
672 // a new requirement with an intersecting version range.
673 else if ((existVersionIdx != -1) && (addVersionIdx != -1))
674 {
675 VersionRange vr = ((VersionRange) existing.getAttributes()[existVersionIdx].getValue())
676 .intersection((VersionRange) additional.getAttributes()[addVersionIdx].getValue());
677 R4Attribute[] attrs = existing.getAttributes();
678 R4Attribute[] newAttrs = new R4Attribute[attrs.length];
679 System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
680 newAttrs[existVersionIdx] = new R4Attribute(
681 attrs[existVersionIdx].getName(), vr, false);
682 intersection = new Requirement(
683 existing.getNamespace(),
684 existing.getDirectives(),
685 newAttrs);
686 }
687
688 return intersection;
689 }
690
691 private void addHost(IModule host)
692 {
693 // When a module is added, we first need to pre-merge any potential fragments
694 // into the host and then second create an aggregated list of unresolved
695 // capabilities to simplify later processing when resolving bundles.
696 m_moduleList.add(host);
697
698 //
699 // First, merge applicable fragments.
700 //
701
702 List fragmentList = getMatchingFragments(host);
703
704 // Attach any fragments we found for this host.
705 if (fragmentList.size() > 0)
706 {
707 // Check if fragment conflicts with existing metadata.
708 checkForConflicts(host, fragmentList);
709
710 // Attach the fragments to the host.
711 IModule[] fragments =
712 (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
713 try
714 {
715 ((ModuleImpl) host).attachFragments(fragments);
716 }
717 catch (Exception ex)
718 {
719 // Try to clean up by removing all fragments.
720 try
721 {
722 ((ModuleImpl) host).attachFragments(null);
723 }
724 catch (Exception ex2)
725 {
726 }
727 m_logger.log(Logger.LOG_ERROR,
728 "Serious error attaching fragments.", ex);
729 }
730 }
731
732 //
733 // Second, index module's capabilities.
734 //
735
736 ICapability[] caps = host.getCapabilities();
737
738 // Add exports to unresolved package map.
739 for (int i = 0; (caps != null) && (i < caps.length); i++)
740 {
741 if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
742 {
743 indexPackageCapability(m_unresolvedPkgIndex, caps[i]);
744 }
745 }
746 }
747
748 private void removeHost(IModule host)
749 {
750 // We need remove the host's exports from the "resolved" and
751 // "unresolved" package maps, remove its dependencies on fragments
752 // and exporters, and remove it from the module list.
753 m_moduleList.remove(host);
754
755 // Remove exports from package maps.
756 ICapability[] caps = host.getCapabilities();
757 for (int i = 0; (caps != null) && (i < caps.length); i++)
758 {
759 if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
760 {
761 // Get package name.
762 String pkgName = (String)
763 caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
764 // Remove from "unresolved" package map.
765 List capList = (List) m_unresolvedPkgIndex.get(pkgName);
766 if (capList != null)
767 {
768 capList.remove(caps[i]);
769 }
770
771 // Remove from "resolved" package map.
772 capList = (List) m_resolvedPkgIndex.get(pkgName);
773 if (capList != null)
774 {
775 capList.remove(caps[i]);
776 }
777 }
778 }
779
780 // Remove the module from the "resolved" map.
781 m_resolvedCapMap.remove(host);
782
783 // Set fragments to null, which will remove the module from all
784 // of its dependent fragment modules.
785 try
786 {
787 ((ModuleImpl) host).attachFragments(null);
788 }
789 catch (Exception ex)
790 {
791 m_logger.log(Logger.LOG_ERROR, "Error detaching fragments.", ex);
792 }
793 // Set wires to null, which will remove the module from all
794 // of its dependent modules.
795 ((ModuleImpl) host).setWires(null);
796 }
797
798 private List getMatchingFragments(IModule host)
799 {
800 // Find the host capability for the current host.
801 ICapability[] caps = Util.getCapabilityByNamespace(host, ICapability.HOST_NAMESPACE);
802 ICapability hostCap = (caps.length == 0) ? null : caps[0];
803
804 // If we have a host capability, then loop through all fragments trying to
805 // find ones that match.
806 List fragmentList = new ArrayList();
807 SecurityManager sm = System.getSecurityManager();
808 if ((sm != null) && (host.getSymbolicName() != null))
809 {
810 if (!((BundleProtectionDomain) host.getSecurityContext()).impliesDirect(new BundlePermission(host.getSymbolicName(), BundlePermission.HOST)))
811 {
812 return fragmentList;
813 }
814 }
815 for (Iterator it = m_fragmentMap.entrySet().iterator(); (hostCap != null) && it.hasNext(); )
816 {
817 Map.Entry entry = (Map.Entry) it.next();
818 List fragments = (List) entry.getValue();
819 IModule fragment = null;
820 for (int i = 0; (fragment == null) && (i < fragments.size()); i++)
821 {
822 IModule f = (IModule) fragments.get(i);
823 if (!((BundleImpl) f.getBundle()).isStale()
824 && !((BundleImpl) f.getBundle()).isRemovalPending())
825 {
826 fragment = f;
827 }
828 }
829
830 if (fragment == null)
831 {
832 continue;
833 }
834
835 if ((sm != null) && (fragment.getSymbolicName() != null))
836 {
837 if (!((BundleProtectionDomain) fragment.getSecurityContext()).impliesDirect(new BundlePermission(fragment.getSymbolicName(), BundlePermission.FRAGMENT)))
838 {
839 continue;
840 }
841 }
842 IRequirement hostReq = getFragmentHostRequirement(fragment);
843
844 // If we have a host requirement, then loop through each host and
845 // see if it matches the host requirement.
846 if ((hostReq != null) && hostReq.isSatisfied(hostCap))
847 {
848 // Now add the new fragment in bundle ID order.
849 int index = -1;
850 for (int listIdx = 0;
851 (index < 0) && (listIdx < fragmentList.size());
852 listIdx++)
853 {
854 IModule existing = (IModule) fragmentList.get(listIdx);
855 if (fragment.getBundle().getBundleId()
856 < existing.getBundle().getBundleId())
857 {
858 index = listIdx;
859 }
860 }
861 fragmentList.add(
862 (index < 0) ? fragmentList.size() : index, fragment);
863 }
864 }
865
866 return fragmentList;
867 }
868
869 public synchronized IModule findHost(IModule rootModule) throws ResolveException
870 {
871 IModule newRootModule = rootModule;
872 if (Util.isFragment(rootModule))
873 {
874 List matchingHosts = getMatchingHosts(rootModule);
875 IModule currentBestHost = null;
876 for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
877 {
878 IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
879 if (currentBestHost == null)
880 {
881 currentBestHost = host;
882 }
883 else if (currentBestHost.getVersion().compareTo(host.getVersion()) < 0)
884 {
885 currentBestHost = host;
886 }
887 }
888 newRootModule = currentBestHost;
889
890 if (newRootModule == null)
891 {
892 throw new ResolveException(
893 "Unable to find host.", rootModule, getFragmentHostRequirement(rootModule));
894 }
895 }
896
897 return newRootModule;
898 }
899
900 private IRequirement getFragmentHostRequirement(IModule fragment)
901 {
902 // Find the fragment's host requirement.
903 IRequirement[] reqs = fragment.getRequirements();
904 IRequirement hostReq = null;
905 for (int reqIdx = 0; (hostReq == null) && (reqIdx < reqs.length); reqIdx++)
906 {
907 if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
908 {
909 hostReq = reqs[reqIdx];
910 }
911 }
912 return hostReq;
913 }
914
915 /**
916 * This method is used for installing system bundle extensions. It actually
917 * refreshes the system bundle module's capabilities in the resolver state
918 * to capture additional capabilities.
919 * @param module The module being refresh, which should always be the system bundle.
920 **/
921 synchronized void refreshSystemBundleModule(IModule module)
922 {
923 // The system bundle module should always be resolved, so we only need
924 // to update the resolved capability map.
925 ICapability[] caps = module.getCapabilities();
926 for (int i = 0; (caps != null) && (i < caps.length); i++)
927 {
928 List resolvedCaps = (List) m_resolvedCapMap.get(module);
929 if (resolvedCaps == null)
930 {
931 m_resolvedCapMap.put(module, resolvedCaps = new ArrayList());
932 }
933 if (!resolvedCaps.contains(caps[i]))
934 {
935 resolvedCaps.add(caps[i]);
936 }
937
938 // If the capability is a package, then add the exporter module
939 // of the wire to the "resolved" package index and remove it
940 // from the "unresolved" package index.
941 if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
942 {
943 // Add to "resolved" package index.
944 indexPackageCapability(m_resolvedPkgIndex, caps[i]);
945 }
946 }
947 }
948
949 private void dumpPackageIndex(Map pkgIndex)
950 {
951 for (Iterator i = pkgIndex.entrySet().iterator(); i.hasNext(); )
952 {
953 Map.Entry entry = (Map.Entry) i.next();
954 List capList = (List) entry.getValue();
955 if (capList.size() > 0)
956 {
957 if (!((capList.size() == 1) && ((ICapability) capList.get(0)).getModule().getId().equals("0")))
958 {
959 System.out.println(" " + entry.getKey());
960 for (int j = 0; j < capList.size(); j++)
961 {
962 System.out.println(" " + ((ICapability) capList.get(j)).getModule());
963 }
964 }
965 }
966 }
967 }
968
969 public synchronized IModule[] getModules()
970 {
971 return (IModule[]) m_moduleList.toArray(new IModule[m_moduleList.size()]);
972 }
973
974 public synchronized void moduleResolved(IModule module)
975 {
976 if (module.isResolved())
977 {
978 // At this point, we need to remove all of the resolved module's
979 // capabilities from the "unresolved" package map and put them in
980 // in the "resolved" package map, with the exception of any
981 // package exports that are also imported. In that case we need
982 // to make sure that the import actually points to the resolved
983 // module and not another module. If it points to another module
984 // then the capability should be ignored, since the framework
985 // decided to honor the import and discard the export.
986 ICapability[] caps = module.getCapabilities();
987
988 // First remove all existing capabilities from the "unresolved" map.
989 for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
990 {
991 if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
992 {
993 // Get package name.
994 String pkgName = (String)
995 caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
996 // Remove the module's capability for the package.
997 List capList = (List) m_unresolvedPkgIndex.get(pkgName);
998 capList.remove(caps[capIdx]);
999 }
1000 }
1001
1002 // Next create a copy of the module's capabilities so we can
1003 // null out any capabilities that should be ignored.
1004 ICapability[] capsCopy = (caps == null) ? null : new ICapability[caps.length];
1005 if (capsCopy != null)
1006 {
1007 System.arraycopy(caps, 0, capsCopy, 0, caps.length);
1008 }
1009 // Loop through the module's capabilities to determine which ones
1010 // can be ignored by seeing which ones satifies the wire requirements.
1011 // TODO: RB - Bug here because a requirement for a package need not overlap the
1012 // capability for that package and this assumes it does. This might
1013 // require us to introduce the notion of a substitutable capability.
1014 IWire[] wires = module.getWires();
1015 for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
1016 {
1017 // Loop through all wires to see if the current capability
1018 // satisfies any of the wire requirements.
1019 for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
1020 {
1021 // If one of the module's capabilities satifies the requirement
1022 // for an existing wire, this means the capability was
1023 // substituted with another provider by the resolver and
1024 // the module's capability was not used. Therefore, we should
1025 // null it here so it doesn't get added the list of resolved
1026 // capabilities for this module.
1027 if (wires[wireIdx].getRequirement().isSatisfied(capsCopy[capIdx]))
1028 {
1029 capsCopy[capIdx] = null;
1030 break;
1031 }
1032 }
1033 }
1034
1035 // Now loop through all capabilities and add them to the "resolved"
1036 // capability and package index maps, ignoring any that were nulled out.
1037 for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
1038 {
1039 if (capsCopy[capIdx] != null)
1040 {
1041 List resolvedCaps = (List) m_resolvedCapMap.get(module);
1042 if (resolvedCaps == null)
1043 {
1044 m_resolvedCapMap.put(module, resolvedCaps = new ArrayList());
1045 }
1046 if (!resolvedCaps.contains(capsCopy[capIdx]))
1047 {
1048 resolvedCaps.add(capsCopy[capIdx]);
1049 }
1050
1051 // If the capability is a package, then add the exporter module
1052 // of the wire to the "resolved" package index and remove it
1053 // from the "unresolved" package index.
1054 if (capsCopy[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
1055 {
1056 // Add to "resolved" package index.
1057 indexPackageCapability(m_resolvedPkgIndex, capsCopy[capIdx]);
1058 }
1059 }
1060 }
1061 }
1062
1063 //System.out.println("UNRESOLVED PACKAGES:");
1064 //dumpPackageIndex(m_unresolvedPkgIndex);
1065 //System.out.println("RESOLVED PACKAGES:");
1066 //dumpPackageIndex(m_resolvedPkgIndex);
1067 }
1068
1069 public synchronized List getResolvedCandidates(IRequirement req, IModule reqModule)
1070 {
1071 // Synchronized on the module manager to make sure that no
1072 // modules are added, removed, or resolved.
1073 List candidates = new ArrayList();
1074 if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
1075 && (((Requirement) req).getTargetName() != null))
1076 {
1077 String pkgName = ((Requirement) req).getTargetName();
1078 List capList = (List) m_resolvedPkgIndex.get(pkgName);
1079
1080 for (int capIdx = 0; (capList != null) && (capIdx < capList.size()); capIdx++)
1081 {
1082 ICapability cap = (ICapability) capList.get(capIdx);
1083 if (req.isSatisfied(cap))
1084 {
1085 if (System.getSecurityManager() != null)
1086 {
1087 if (reqModule != ((ICapability) capList.get(capIdx)).getModule())
1088 {
1089 if ((!((BundleProtectionDomain)((ICapability)
1090 capList.get(capIdx)).getModule().getSecurityContext()).impliesDirect(
1091 new PackagePermission(((Requirement) req).getTargetName(), PackagePermission.EXPORTONLY))) ||
1092 !((reqModule == null) ||
1093 ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
1094 new PackagePermission(((Requirement) req).getTargetName(), ((ICapability)
1095 capList.get(capIdx)).getModule().getBundle(),PackagePermission.IMPORT))
1096 ))
1097 {
1098 continue;
1099 }
1100 }
1101 }
1102 candidates.add(cap);
1103 }
1104 }
1105 }
1106 else
1107 {
1108 Iterator i = m_resolvedCapMap.entrySet().iterator();
1109 while (i.hasNext())
1110 {
1111 Map.Entry entry = (Map.Entry) i.next();
1112 IModule module = (IModule) entry.getKey();
1113 List caps = (List) entry.getValue();
1114 for (int capIdx = 0; (caps != null) && (capIdx < caps.size()); capIdx++)
1115 {
1116 ICapability cap = (ICapability) caps.get(capIdx);
1117 if (req.isSatisfied(cap))
1118 {
1119 if (System.getSecurityManager() != null)
1120 {
1121 if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) && (
1122 !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
1123 new PackagePermission((String) cap.getProperties().get(ICapability.PACKAGE_PROPERTY), PackagePermission.EXPORTONLY)) ||
1124 !((reqModule == null) ||
1125 ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
1126 new PackagePermission((String) cap.getProperties().get(ICapability.PACKAGE_PROPERTY), cap.getModule().getBundle(),PackagePermission.IMPORT))
1127 )))
1128 {
1129 if (reqModule != cap.getModule())
1130 {
1131 continue;
1132 }
1133 }
1134 if (req.getNamespace().equals(ICapability.MODULE_NAMESPACE) && (
1135 !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
1136 new BundlePermission(cap.getModule().getSymbolicName(), BundlePermission.PROVIDE)) ||
1137 !((reqModule == null) ||
1138 ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
1139 new BundlePermission(reqModule.getSymbolicName(), BundlePermission.REQUIRE))
1140 )))
1141 {
1142 continue;
1143 }
1144 }
1145 candidates.add(cap);
1146 }
1147 }
1148 }
1149 }
1150 Collections.sort(candidates);
1151 return candidates;
1152 }
1153
1154 public synchronized List getUnresolvedCandidates(IRequirement req, IModule reqModule)
1155 {
1156 // Get all matching unresolved capabilities.
1157 List candidates = new ArrayList();
1158 if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
1159 (((Requirement) req).getTargetName() != null))
1160 {
1161 List capList = (List) m_unresolvedPkgIndex.get(((Requirement) req).getTargetName());
1162 for (int capIdx = 0; (capList != null) && (capIdx < capList.size()); capIdx++)
1163 {
1164 // If compatible and it is not currently resolved, then add
1165 // the unresolved candidate to the list.
1166 if (req.isSatisfied((ICapability) capList.get(capIdx)))
1167 {
1168 if (System.getSecurityManager() != null)
1169 {
1170 if (reqModule != ((ICapability) capList.get(capIdx)).getModule())
1171 {
1172 if (!((BundleProtectionDomain)((ICapability)
1173 capList.get(capIdx)).getModule().getSecurityContext()).impliesDirect(
1174 new PackagePermission(((Requirement) req).getTargetName(), PackagePermission.EXPORTONLY)) ||
1175 !((reqModule == null) ||
1176 ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
1177 new PackagePermission(((Requirement) req).getTargetName(), ((ICapability)
1178 capList.get(capIdx)).getModule().getBundle(),PackagePermission.IMPORT))
1179 ))
1180 {
1181 continue;
1182 }
1183 }
1184 }
1185 candidates.add(capList.get(capIdx));
1186 }
1187 }
1188 }
1189 else
1190 {
1191 IModule[] modules = getModules();
1192 for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
1193 {
1194 // Get the module's export package for the target package.
1195 ICapability cap = Util.getSatisfyingCapability(modules[modIdx], req);
1196 // If compatible and it is not currently resolved, then add
1197 // the unresolved candidate to the list.
1198 if ((cap != null) && !modules[modIdx].isResolved())
1199 {
1200 if (System.getSecurityManager() != null)
1201 {
1202 if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) && (
1203 !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
1204 new PackagePermission((String) cap.getProperties().get(ICapability.PACKAGE_PROPERTY), PackagePermission.EXPORTONLY)) ||
1205 !((reqModule == null) ||
1206 ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
1207 new PackagePermission((String) cap.getProperties().get(ICapability.PACKAGE_PROPERTY), cap.getModule().getBundle(),PackagePermission.IMPORT))
1208 )))
1209 {
1210 if (reqModule != cap.getModule())
1211 {
1212 continue;
1213 }
1214 }
1215 if (req.getNamespace().equals(ICapability.MODULE_NAMESPACE) && (
1216 !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
1217 new BundlePermission(cap.getModule().getSymbolicName(), BundlePermission.PROVIDE)) ||
1218 !((reqModule == null) ||
1219 ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
1220 new BundlePermission(reqModule.getSymbolicName(), BundlePermission.REQUIRE))
1221 )))
1222 {
1223 continue;
1224 }
1225 }
1226 candidates.add(cap);
1227 }
1228 }
1229 }
1230
1231 // Create list of compatible providers.
1232 Collections.sort(candidates);
1233 return candidates;
1234 }
1235
1236 //
1237 // Utility methods.
1238 //
1239
1240 private void indexPackageCapability(Map map, ICapability capability)
1241 {
1242 if (capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
1243 {
1244 String pkgName = (String)
1245 capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
1246 List capList = (List) map.get(pkgName);
1247
1248 // We want to add the capability into the list of exporters
1249 // in sorted order (descending version and ascending bundle
1250 // identifier). Insert using a simple binary search algorithm.
1251 if (capList == null)
1252 {
1253 capList = new ArrayList();
1254 capList.add(capability);
1255 }
1256 else
1257 {
1258 Version version = (Version)
1259 capability.getProperties().get(ICapability.VERSION_PROPERTY);
1260 Version middleVersion = null;
1261 int top = 0, bottom = capList.size() - 1, middle = 0;
1262 while (top <= bottom)
1263 {
1264 middle = (bottom - top) / 2 + top;
1265 middleVersion = (Version)
1266 ((ICapability) capList.get(middle))
1267 .getProperties().get(ICapability.VERSION_PROPERTY);
1268 // Sort in reverse version order.
1269 int cmp = middleVersion.compareTo(version);
1270 if (cmp < 0)
1271 {
1272 bottom = middle - 1;
1273 }
1274 else if (cmp == 0)
1275 {
1276 // Sort further by ascending bundle ID.
1277 long middleId = ((ICapability) capList.get(middle))
1278 .getModule().getBundle().getBundleId();
1279 long exportId = capability.getModule().getBundle().getBundleId();
1280 if (middleId < exportId)
1281 {
1282 top = middle + 1;
1283 }
1284 else
1285 {
1286 bottom = middle - 1;
1287 }
1288 }
1289 else
1290 {
1291 top = middle + 1;
1292 }
1293 }
1294
1295 // Ignore duplicates.
1296 if ((top >= capList.size()) || (capList.get(top) != capability))
1297 {
1298 capList.add(top, capability);
1299 }
1300 }
1301
1302 map.put(pkgName, capList);
1303 }
1304 }
1305
1306 private IModule indexFragment(Map map, IModule module)
1307 {
1308 List modules = (List) map.get(module.getSymbolicName());
1309
1310 // We want to add the fragment into the list of matching
1311 // fragments in sorted order (descending version and
1312 // ascending bundle identifier). Insert using a simple
1313 // binary search algorithm.
1314 if (modules == null)
1315 {
1316 modules = new ArrayList();
1317 modules.add(module);
1318 }
1319 else
1320 {
1321 Version version = module.getVersion();
1322 Version middleVersion = null;
1323 int top = 0, bottom = modules.size() - 1, middle = 0;
1324 while (top <= bottom)
1325 {
1326 middle = (bottom - top) / 2 + top;
1327 middleVersion = ((IModule) modules.get(middle)).getVersion();
1328 // Sort in reverse version order.
1329 int cmp = middleVersion.compareTo(version);
1330 if (cmp < 0)
1331 {
1332 bottom = middle - 1;
1333 }
1334 else if (cmp == 0)
1335 {
1336 // Sort further by ascending bundle ID.
1337 long middleId = ((IModule) modules.get(middle)).getBundle().getBundleId();
1338 long exportId = module.getBundle().getBundleId();
1339 if (middleId < exportId)
1340 {
1341 top = middle + 1;
1342 }
1343 else
1344 {
1345 bottom = middle - 1;
1346 }
1347 }
1348 else
1349 {
1350 top = middle + 1;
1351 }
1352 }
1353
1354 // Ignore duplicates.
1355 if ((top >= modules.size()) || (modules.get(top) != module))
1356 {
1357 modules.add(top, module);
1358 }
1359 }
1360
1361 map.put(module.getSymbolicName(), modules);
1362
1363 return (IModule) modules.get(0);
1364 }
1365 }