@Override
  public void install(LocalExtension localExtension, String namespace) throws InstallException {
    ExtensionURLClassLoader classLoader =
        this.jarExtensionClassLoader.getURLClassLoader(namespace, true);

    // 1) load jar into classloader
    try {
      classLoader.addURL(localExtension.getFile().toURI().toURL());
    } catch (MalformedURLException e) {
      throw new InstallException("Failed to load jar file", e);
    }

    // 2) load and register components
    loadComponents(localExtension.getFile(), classLoader);
  }
  // TODO: support question/answer with the UI to resolve conflicts
  @Override
  public void upgrade(
      LocalExtension previousLocalExtension, LocalExtension newLocalExtension, String namespace)
      throws InstallException {
    // TODO
    // 1) find all modified pages between old and new version
    // 2) compare old version and wiki (to find pages modified by user)
    // 3) delete pages removed in new version (even if modified ?)
    // 4) merge xar
    // 4.1) merge modified pages in wiki with diff between old/new version
    // 4.2) update unmodified pages different between old and new version

    // CURRENT

    XarLocalExtension previousXarExtension;
    try {
      previousXarExtension =
          (XarLocalExtension) this.xarRepository.resolve(previousLocalExtension.getId());
    } catch (ResolveException e) {
      // Not supposed to be possible
      throw new InstallException(
          "Failed to get xar extension ["
              + previousLocalExtension.getId()
              + "] from xar repository",
          e);
    }

    // Install new pages
    install(previousXarExtension, newLocalExtension, namespace);

    // Uninstall old version pages not anymore in the new version
    Set<XarEntry> previousPages = new HashSet<XarEntry>(previousXarExtension.getPages());

    List<XarEntry> newPages;
    try {
      XarLocalExtension newXarExtension =
          (XarLocalExtension) this.xarRepository.resolve(newLocalExtension.getId());
      newPages = newXarExtension.getPages();
    } catch (ResolveException e) {
      try {
        newPages =
            this.packager.getEntries(new File(newLocalExtension.getFile().getAbsolutePath()));
      } catch (IOException e1) {
        throw new InstallException(
            "Failed to get xar extension [" + newLocalExtension.getId() + "] pages", e);
      }
    }

    for (XarEntry entry : newPages) {
      previousPages.remove(entry);
    }

    try {
      this.packager.unimportPages(previousPages, namespace);
    } catch (Exception e) {
      this.logger.warn(
          "Exception when cleaning pages removed since previous xar extension version", e);
    }
  }
  @Override
  public void uninstall(LocalExtension localExtension, String namespace) throws UninstallException {
    ExtensionURLClassLoader classLoader =
        this.jarExtensionClassLoader.getURLClassLoader(namespace, false);

    if (namespace == null || classLoader.getWiki().equals(namespace)) {
      // unregister components
      unloadComponents(localExtension.getFile(), classLoader);

      // TODO: find a way to unload the jar from the classloader
    }
  }
  public void install(
      XarLocalExtension previousExtension, LocalExtension localExtension, String wiki)
      throws InstallException {
    // TODO: should be configurable
    MergeConfiguration mergeConfiguration = new MergeConfiguration();

    // import xar into wiki (add new version when the page already exists)
    try {
      this.packager.importXAR(
          previousExtension != null
              ? new XarFile(
                  new File(previousExtension.getFile().getAbsolutePath()),
                  previousExtension.getPages())
              : null,
          new File(localExtension.getFile().getAbsolutePath()),
          wiki,
          mergeConfiguration);
    } catch (Exception e) {
      throw new InstallException("Failed to import xar for extension [" + localExtension + "]", e);
    }
  }