Browse Source

AMBARI-5702 - Ambari Views : Ability to work on a view as exploded to support UI dev

tbeerbower 11 năm trước cách đây
mục cha
commit
848e951979

+ 9 - 3
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java

@@ -71,6 +71,7 @@ import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.utils.StageUtils;
 import org.apache.ambari.server.utils.VersionUtils;
 import org.apache.ambari.server.view.ViewRegistry;
+import org.apache.ambari.view.SystemException;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.HandlerList;
@@ -288,10 +289,15 @@ public class AmbariServer {
 
       HandlerList handlerList = new HandlerList();
 
-      ViewRegistry viewRegistry = ViewRegistry.getInstance();
-      for (ViewInstanceEntity entity : viewRegistry.readViewArchives(configs)){
-        handlerList.addHandler(viewRegistry.getWebAppContext(entity));
+      try {
+        ViewRegistry viewRegistry = ViewRegistry.getInstance();
+        for (ViewInstanceEntity entity : viewRegistry.readViewArchives(configs)){
+          handlerList.addHandler(viewRegistry.getWebAppContext(entity));
+        }
+      } catch (SystemException e) {
+        LOG.error("Caught exception deploying views.", e);
       }
+
       handlerList.addHandler(root);
 
       server.setHandler(handlerList);

+ 276 - 51
ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java

@@ -20,7 +20,6 @@ package org.apache.ambari.server.view;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
-import com.google.inject.Inject;
 import com.google.inject.Injector;
 import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl;
 import org.apache.ambari.server.api.resources.SubResourceDefinition;
@@ -44,6 +43,7 @@ import org.apache.ambari.server.view.configuration.PersistenceConfig;
 import org.apache.ambari.server.view.configuration.PropertyConfig;
 import org.apache.ambari.server.view.configuration.ResourceConfig;
 import org.apache.ambari.server.view.configuration.ViewConfig;
+import org.apache.ambari.view.SystemException;
 import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.ViewResourceHandler;
 import org.eclipse.jetty.webapp.WebAppContext;
@@ -51,19 +51,28 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
 import javax.xml.bind.Unmarshaller;
 import java.beans.IntrospectionException;
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
 
 /**
  * Registry for view and view instance definitions.
@@ -74,6 +83,9 @@ public class ViewRegistry {
    * Constants
    */
   private static final String VIEW_XML = "view.xml";
+  private static final String ARCHIVE_CLASSES_DIR = "WEB-INF/classes";
+  private static final String ARCHIVE_LIB_DIR = "WEB-INF/lib";
+  private static final String EXTRACTED_ARCHIVES_DIR = "work";
 
   /**
    * Mapping of view names to view definitions.
@@ -83,12 +95,19 @@ public class ViewRegistry {
   /**
    * Mapping of view instances to view definition and instance name.
    */
-  private Map<ViewEntity, Map<String, ViewInstanceEntity>> viewInstanceDefinitions = new HashMap<ViewEntity, Map<String, ViewInstanceEntity>>();
+  private Map<ViewEntity, Map<String, ViewInstanceEntity>> viewInstanceDefinitions =
+      new HashMap<ViewEntity, Map<String, ViewInstanceEntity>>();
 
   /**
    * Mapping of view names to sub-resources.
    */
-  private final Map<String, Set<SubResourceDefinition>> subResourceDefinitionsMap = new HashMap<String, Set<SubResourceDefinition>>();
+  private final Map<String, Set<SubResourceDefinition>> subResourceDefinitionsMap =
+      new HashMap<String, Set<SubResourceDefinition>>();
+
+  /**
+   * Helper class.
+   */
+  private ViewRegistryHelper helper = new ViewRegistryHelper();
 
   /**
    * The singleton view registry instance.
@@ -110,18 +129,6 @@ public class ViewRegistry {
    */
   private static ViewInstanceDAO instanceDAO;
 
-  /**
-   * Static initialization of DAO.
-   *
-   * @param vDAO  view data access object
-   * @param iDAO  view instance data access object
-   */
-  @Inject
-  public static void init(ViewDAO vDAO, ViewInstanceDAO iDAO) {
-    viewDAO     = vDAO;
-    instanceDAO = iDAO;
-  }
-
 
   // ----- Constructors ------------------------------------------------------
 
@@ -267,48 +274,60 @@ public class ViewRegistry {
    * Read the view archives.
    *
    * @param configuration  Ambari configuration
+   *
+   * @throws SystemException if the view archives can not be successfully read
    */
-  public Set<ViewInstanceEntity> readViewArchives(Configuration configuration) {
+  public Set<ViewInstanceEntity> readViewArchives(Configuration configuration)
+      throws SystemException {
 
-    File   viewDir = configuration.getViewsDir();
-    File[] files   = viewDir.listFiles();
+    try {
+      File viewDir = configuration.getViewsDir();
 
-    Set<ViewInstanceEntity> instanceDefinitions = new HashSet<ViewInstanceEntity>();
+      Set<ViewInstanceEntity> instanceDefinitions = new HashSet<ViewInstanceEntity>();
 
-    if (files != null) {
-      for (final File fileEntry : files) {
-        if (!fileEntry.isDirectory()) {
-          try {
-            ClassLoader cl = URLClassLoader.newInstance(new URL[]{fileEntry.toURI().toURL()});
+      String extractedArchivesPath = viewDir.getAbsolutePath() +
+          File.separator + EXTRACTED_ARCHIVES_DIR;
 
-            InputStream  configStream     = cl.getResourceAsStream(VIEW_XML);
-            JAXBContext  jaxbContext      = JAXBContext.newInstance(ViewConfig.class);
-            Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
-            ViewConfig   viewConfig       = (ViewConfig) jaxbUnmarshaller.unmarshal(configStream);
-            ViewEntity   viewDefinition   = installView(viewConfig, configuration, cl, fileEntry.getAbsolutePath());
+      if (ensureExtractedArchiveDirectory(extractedArchivesPath)) {
+        File[] files = viewDir.listFiles();
 
-            for (InstanceConfig instanceConfig : viewConfig.getInstances()) {
-              ViewInstanceEntity viewInstanceDefinition = new ViewInstanceEntity(viewDefinition, instanceConfig);
+        if (files != null) {
+          for (File archiveFile : files) {
+            if (!archiveFile.isDirectory()) {
+              try {
+                ViewConfig viewConfig = helper.getViewConfigFromArchive(archiveFile);
 
-              for (PropertyConfig propertyConfig : instanceConfig.getProperties()) {
-                viewInstanceDefinition.putProperty(propertyConfig.getKey(), propertyConfig.getValue());
-              }
+                String viewName    = ViewEntity.getViewName(viewConfig.getName(), viewConfig.getVersion());
+                String archivePath = extractedArchivesPath + File.separator + viewName;
 
-              installViewInstance(viewDefinition, viewInstanceDefinition);
-              instanceDefinitions.add(viewInstanceDefinition);
+                // extract the archive and get the class loader
+                ClassLoader cl = extractViewArchive(archiveFile, helper.getFile(archivePath));
+
+                ViewEntity viewDefinition = installView(viewConfig, configuration, cl, archivePath);
+
+                for (InstanceConfig instanceConfig : viewConfig.getInstances()) {
+                  ViewInstanceEntity viewInstanceDefinition =
+                      new ViewInstanceEntity(viewDefinition, instanceConfig);
+
+                  for (PropertyConfig propertyConfig : instanceConfig.getProperties()) {
+                    viewInstanceDefinition.putProperty(propertyConfig.getKey(), propertyConfig.getValue());
+                  }
+
+                  installViewInstance(viewDefinition, viewInstanceDefinition);
+                  instanceDefinitions.add(viewInstanceDefinition);
+                }
+              } catch (Exception e) {
+                LOG.error("Caught exception loading view from " + archiveFile.getAbsolutePath(), e);
+              }
             }
-          } catch (Exception e) {
-            LOG.error("Caught exception loading view from " + fileEntry.getAbsolutePath(), e);
           }
+          instanceDefinitions.addAll(persistViews());
         }
       }
-      try {
-        instanceDefinitions.addAll(persistViews());
-      } catch (ClassNotFoundException e) {
-        LOG.error("Caught exception persisting views.", e);
-      }
+      return instanceDefinitions;
+    } catch (Exception e) {
+      throw new SystemException("Caught exception reading view archives.", e);
     }
-    return instanceDefinitions;
   }
 
   /**
@@ -415,14 +434,22 @@ public class ViewRegistry {
    * @param viewInstanceDefinition  the view instance definition
    *
    * @return a web app context
+   *
+   * @throws SystemException if an application context can not be obtained for the given view instance
    */
-  public WebAppContext getWebAppContext(ViewInstanceEntity viewInstanceDefinition) {
-    ViewEntity viewDefinition = viewInstanceDefinition.getViewEntity();
-
-    WebAppContext context = new WebAppContext(viewDefinition.getArchive(), viewInstanceDefinition.getContextPath());
-    context.setClassLoader(viewDefinition.getClassLoader());
-    context.setAttribute(ViewContext.CONTEXT_ATTRIBUTE, new ViewContextImpl(viewInstanceDefinition, this));
-    return context;
+  public WebAppContext getWebAppContext(ViewInstanceEntity viewInstanceDefinition)
+      throws SystemException{
+    try {
+      ViewEntity viewDefinition = viewInstanceDefinition.getViewEntity();
+
+      WebAppContext context = new WebAppContext(viewDefinition.getArchive(), viewInstanceDefinition.getContextPath());
+      context.setClassLoader(viewDefinition.getClassLoader());
+      context.setAttribute(ViewContext.CONTEXT_ATTRIBUTE, new ViewContextImpl(viewInstanceDefinition, this));
+      return context;
+    } catch (Exception e) {
+      throw new SystemException("Can't get application context for view " +
+          viewInstanceDefinition.getViewEntity().getCommonName() + ".", e);
+    }
   }
 
 
@@ -437,6 +464,15 @@ public class ViewRegistry {
     subResourceDefinitionsMap.clear();
   }
 
+  /**
+   * Set the helper.
+   *
+   * @param helper  the helper
+   */
+  protected void setHelper(ViewRegistryHelper helper) {
+    this.helper = helper;
+  }
+
   // get a view entity for the given internal view name
   private ViewEntity getDefinition(String viewName) {
     return viewDefinitions.get(viewName);
@@ -654,4 +690,193 @@ public class ViewRegistry {
     }
     return instanceDefinitions;
   }
+
+  // ensure that the extracted view archive directory exists
+  private boolean ensureExtractedArchiveDirectory(String extractedArchivesPath) {
+    File extractedArchiveDir = helper.getFile(extractedArchivesPath);
+
+    if (!extractedArchiveDir.exists()) {
+      if (!extractedArchiveDir.mkdir()) {
+        LOG.error("Could not create extracted view archive directory " +
+            extractedArchivesPath + ".");
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // extract the given view archive to the given archive directory
+  private ClassLoader extractViewArchive(File viewArchive, File archiveDir) 
+      throws IOException {
+
+    // Skip if the archive has already been extracted
+    if (!archiveDir.exists()) {
+
+      String archivePath = archiveDir.getAbsolutePath();
+
+      LOG.info("Creating archive folder " + archivePath + ".");
+      
+      if (archiveDir.mkdir()) {
+        JarFile     viewJarFile = helper.getJarFile(viewArchive);
+        Enumeration enumeration = viewJarFile.entries();
+
+        LOG.info("Extracting files from " + viewArchive.getName() + ":");
+
+        while (enumeration.hasMoreElements()) {
+          JarEntry jarEntry  = (JarEntry) enumeration.nextElement();
+          String   entryPath = archivePath + File.separator + jarEntry.getName();
+
+          LOG.info("    " + entryPath);
+
+          File entryFile = helper.getFile(entryPath);
+
+          if (jarEntry.isDirectory()) {
+            if (!entryFile.mkdir()) {
+              LOG.error("Could not create archive entry directory " + entryPath + ".");
+            }
+          } else {
+            InputStream is = viewJarFile.getInputStream(jarEntry);
+            try {
+              FileOutputStream fos = helper.getFileOutputStream(entryFile);
+              try {
+                while (is.available() > 0) {
+                  fos.write(is.read());
+                }
+              } finally {
+                fos.close();
+              }
+            } finally {
+              is.close();
+            }
+          }
+        }
+      } else {
+        LOG.error("Could not create archive directory " + archivePath + ".");
+      }
+    }
+    return getArchiveClassLoader(archiveDir);
+  }
+
+  // get a class loader for the given archive directory
+  private ClassLoader getArchiveClassLoader(File archiveDir) 
+      throws MalformedURLException {
+
+    String    archivePath = archiveDir.getAbsolutePath();
+    List<URL> urlList     = new LinkedList<URL>();
+
+    // include the classes directory
+    String classesPath = archivePath + File.separator + ARCHIVE_CLASSES_DIR;
+    File   classesDir  = helper.getFile(classesPath);
+    if (classesDir.exists()) {
+      urlList.add(classesDir.toURI().toURL());
+    }
+
+    // include any libraries in the lib directory
+    String libPath = archivePath + File.separator + ARCHIVE_LIB_DIR;
+    File   libDir  = helper.getFile(libPath);
+    if (libDir.exists()) {
+      File[] files = libDir.listFiles();
+      if (files != null) {
+        for (final File fileEntry : files) {
+          if (!fileEntry.isDirectory()) {
+            urlList.add(fileEntry.toURI().toURL());
+          }
+        }
+      }
+    }
+
+    // include the archive directory 
+    urlList.add(archiveDir.toURI().toURL());
+
+    return URLClassLoader.newInstance(urlList.toArray(new URL[urlList.size()]));
+  }
+
+  /**
+   * Static initialization of DAO.
+   *
+   * @param viewDAO      view data access object
+   * @param instanceDAO  view instance data access object
+   */
+  public static void init(ViewDAO viewDAO, ViewInstanceDAO instanceDAO) {
+    setViewDAO(viewDAO);
+    setInstanceDAO(instanceDAO);
+  }
+
+  /**
+   * Set the view DAO.
+   *
+   * @param viewDAO  the view DAO
+   */
+  protected static void setViewDAO(ViewDAO viewDAO) {
+    ViewRegistry.viewDAO = viewDAO;
+  }
+
+  /**
+   * Set the instance DAO.
+   *
+   * @param instanceDAO  the instance DAO
+   */
+  protected static void setInstanceDAO(ViewInstanceDAO instanceDAO) {
+    ViewRegistry.instanceDAO = instanceDAO;
+  }
+
+
+  // ----- inner class : ViewRegistryHelper ----------------------------------
+
+  /**
+   * Registry helper class.
+   */
+  protected static class ViewRegistryHelper {
+
+    /**
+     * Get the view configuration from the given archive file.
+     *
+     * @param archiveFile  the archive file
+     *
+     * @return the associated view configuration
+     */
+    public ViewConfig getViewConfigFromArchive(File archiveFile)
+        throws MalformedURLException, JAXBException {
+      ClassLoader cl = URLClassLoader.newInstance(new URL[]{archiveFile.toURI().toURL()});
+
+      InputStream  configStream     = cl.getResourceAsStream(VIEW_XML);
+      JAXBContext  jaxbContext      = JAXBContext.newInstance(ViewConfig.class);
+      Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
+
+      return (ViewConfig) jaxbUnmarshaller.unmarshal(configStream);
+    }
+
+    /**
+     * Get a new file instance for the given path.
+     *
+     * @param path  the path
+     *
+     * @return a new file instance
+     */
+    public File getFile(String path) {
+      return new File(path);
+    }
+
+    /**
+     * Get a new file output stream for the given file.
+     *
+     * @param file  the file
+     *
+     * @return a new file output stream
+     */
+    public FileOutputStream getFileOutputStream(File file) throws FileNotFoundException {
+      return new FileOutputStream(file);
+    }
+
+    /**
+     * Get a new jar file instance from the given file.
+     *
+     * @param file  the file
+     *
+     * @return a new jar file instance
+     */
+    public JarFile getJarFile(File file) throws IOException {
+      return new JarFile(file);
+    }
+  }
 }

+ 165 - 0
ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java

@@ -19,6 +19,7 @@
 package org.apache.ambari.server.view;
 
 import org.apache.ambari.server.api.resources.SubResourceDefinition;
+import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.orm.dao.ViewDAO;
@@ -32,14 +33,32 @@ import org.apache.ambari.server.view.configuration.InstanceConfig;
 import org.apache.ambari.server.view.configuration.InstanceConfigTest;
 import org.apache.ambari.server.view.configuration.ResourceConfig;
 import org.apache.ambari.server.view.configuration.ResourceConfigTest;
+import org.apache.ambari.server.view.configuration.ViewConfig;
+import org.easymock.Capture;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
+import javax.xml.bind.JAXBException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
 
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createNiceMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
@@ -49,6 +68,114 @@ import static org.easymock.EasyMock.verify;
  * ViewRegistry tests.
  */
 public class ViewRegistryTest {
+
+  @Test
+  public void testReadViewArchives() throws Exception {
+    Configuration configuration = createNiceMock(Configuration.class);
+    File viewDir = createNiceMock(File.class);
+    File extractedArchiveDir = createNiceMock(File.class);
+    File viewArchive = createNiceMock(File.class);
+    File archiveDir = createNiceMock(File.class);
+    File entryFile  = createNiceMock(File.class);
+    File classesDir = createNiceMock(File.class);
+    File libDir = createNiceMock(File.class);
+    File fileEntry = createNiceMock(File.class);
+
+    JarFile viewJarFile = createNiceMock(JarFile.class);
+    Enumeration<JarEntry> enumeration = createMock(Enumeration.class);
+    JarEntry jarEntry = createNiceMock(JarEntry.class);
+    InputStream is = createMock(InputStream.class);
+    FileOutputStream fos = createMock(FileOutputStream.class);
+
+    ViewDAO vDAO = createMock(ViewDAO.class);
+
+    ViewRegistry.setViewDAO(vDAO);
+
+    ViewEntity viewDefinition = ViewEntityTest.getViewEntity();
+
+    Map<File, ViewConfig> viewConfigs =
+        Collections.singletonMap(viewArchive, viewDefinition.getConfiguration());
+
+    Map<String, File> files = new HashMap<String, File>();
+
+    files.put("/var/lib/ambari-server/resources/views/work", extractedArchiveDir);
+    files.put("/var/lib/ambari-server/resources/views/work/MY_VIEW{1.0.0}", archiveDir);
+    files.put("/var/lib/ambari-server/resources/views/work/MY_VIEW{1.0.0}/view.xml", entryFile);
+    files.put("/var/lib/ambari-server/resources/views/work/MY_VIEW{1.0.0}/WEB-INF/classes", classesDir);
+    files.put("/var/lib/ambari-server/resources/views/work/MY_VIEW{1.0.0}/WEB-INF/lib", libDir);
+
+    Map<File, FileOutputStream> outputStreams = new HashMap<File, FileOutputStream>();
+    outputStreams.put(entryFile, fos);
+
+    Map<File, JarFile> jarFiles = new HashMap<File, JarFile>();
+    jarFiles.put(viewArchive, viewJarFile);
+
+    // set expectations
+    expect(configuration.getViewsDir()).andReturn(viewDir);
+    expect(viewDir.getAbsolutePath()).andReturn("/var/lib/ambari-server/resources/views");
+
+    expect(viewDir.listFiles()).andReturn(new File[]{viewArchive});
+
+    expect(viewArchive.isDirectory()).andReturn(false);
+
+    expect(archiveDir.exists()).andReturn(false);
+    expect(archiveDir.getAbsolutePath()).andReturn(
+        "/var/lib/ambari-server/resources/views/work/MY_VIEW{1.0.0}").anyTimes();
+    expect(archiveDir.mkdir()).andReturn(true);
+    expect(archiveDir.toURI()).andReturn(new URI("file:./"));
+
+    expect(viewJarFile.entries()).andReturn(enumeration);
+    expect(viewJarFile.getInputStream(jarEntry)).andReturn(is);
+
+    expect(enumeration.hasMoreElements()).andReturn(true);
+    expect(enumeration.hasMoreElements()).andReturn(false);
+    expect(enumeration.nextElement()).andReturn(jarEntry);
+
+    expect(jarEntry.getName()).andReturn("view.xml");
+    expect(jarEntry.isDirectory()).andReturn(false);
+
+    expect(is.available()).andReturn(1);
+    expect(is.available()).andReturn(0);
+
+    expect(is.read()).andReturn(10);
+    fos.write(10);
+
+    fos.close();
+    is.close();
+
+    expect(extractedArchiveDir.exists()).andReturn(false);
+    expect(extractedArchiveDir.mkdir()).andReturn(true);
+
+    expect(classesDir.exists()).andReturn(true);
+    expect(classesDir.toURI()).andReturn(new URI("file:./"));
+
+    expect(libDir.exists()).andReturn(true);
+
+    expect(libDir.listFiles()).andReturn(new File[]{fileEntry});
+    expect(fileEntry.toURI()).andReturn(new URI("file:./"));
+
+    Capture<ViewEntity> captureViewEntity = new Capture<ViewEntity>();
+
+    expect(vDAO.findAll()).andReturn(Collections.<ViewEntity>emptyList());
+    vDAO.create(capture(captureViewEntity));
+
+    // replay mocks
+    replay(configuration, viewDir, extractedArchiveDir, viewArchive, archiveDir, entryFile, classesDir,
+        libDir, fileEntry, viewJarFile, enumeration, jarEntry, is, fos, vDAO);
+
+    ViewRegistry registry = ViewRegistry.getInstance();
+    registry.setHelper(new TestViewRegistryHelper(viewConfigs, files, outputStreams, jarFiles));
+
+    Set<ViewInstanceEntity> instanceEntities = registry.readViewArchives(configuration);
+
+    Assert.assertEquals(2, instanceEntities.size());
+    Assert.assertEquals("MY_VIEW", captureViewEntity.getValue().getCommonName());
+
+    // verify mocks
+    verify(configuration, viewDir, extractedArchiveDir, viewArchive, archiveDir, entryFile, classesDir,
+        libDir, fileEntry, viewJarFile, enumeration, jarEntry, is, fos, vDAO);
+  }
+
   @Test
   public void testAddGetDefinitions() throws Exception {
     ViewEntity viewDefinition = ViewEntityTest.getViewEntity();
@@ -153,10 +280,48 @@ public class ViewRegistryTest {
   @Before
   public void before() throws Exception {
     ViewRegistry.getInstance().clear();
+    ViewRegistry.setViewDAO(null);
   }
 
   @AfterClass
   public static void afterClass() {
     ViewRegistry.getInstance().clear();
+    ViewRegistry.setViewDAO(null);
+  }
+
+  public class TestViewRegistryHelper extends ViewRegistry.ViewRegistryHelper {
+    private final Map<File, ViewConfig> viewConfigs;
+    private final Map<String, File> files;
+    private final Map<File, FileOutputStream> outputStreams;
+    private final Map<File, JarFile> jarFiles;
+
+    public TestViewRegistryHelper(Map<File, ViewConfig> viewConfigs, Map<String, File> files, Map<File,
+        FileOutputStream> outputStreams, Map<File, JarFile> jarFiles) {
+      this.viewConfigs = viewConfigs;
+      this.files = files;
+      this.outputStreams = outputStreams;
+      this.jarFiles = jarFiles;
+    }
+
+    @Override
+    public ViewConfig getViewConfigFromArchive(File archiveFile) throws MalformedURLException, JAXBException {
+      return viewConfigs.get(archiveFile);
+    }
+
+    @Override
+    public File getFile(String path) {
+      return files.get(path);
+    }
+
+    @Override
+    public FileOutputStream getFileOutputStream(File file) throws FileNotFoundException {
+      return outputStreams.get(file);
+    }
+
+    @Override
+    public JarFile getJarFile(File file) throws IOException {
+      return jarFiles.get(file);
+    }
   }
+
 }