Skip to content

Commit

Permalink
Implement ProjectCache for FORGE-1141 - Shell is noticably slow when …
Browse files Browse the repository at this point in the history
…working in project folder
  • Loading branch information
lincolnthree committed Aug 29, 2013
1 parent f827441 commit 1f55c8e
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.jboss.forge.addon.projects;

import org.jboss.forge.addon.projects.spi.ProjectCache;
import org.jboss.forge.addon.resource.DirectoryResource;
import org.jboss.forge.addon.resource.FileResource;
import org.jboss.forge.furnace.services.Exported;
Expand All @@ -32,6 +33,12 @@ public interface ProjectFactory
*/
public Project findProject(final FileResource<?> target, Predicate<Project> filter);

/**
* Invalidate all known {@link ProjectCache} instances. This causes the {@link ProjectFactory} to create new
* instances of requested projects.
*/
public void invalidateCaches();

/**
* Create a {@link Project} in the specified {@link DirectoryResource}.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.forge.addon.projects.spi;

import org.jboss.forge.addon.projects.Project;
import org.jboss.forge.addon.resource.DirectoryResource;
import org.jboss.forge.furnace.services.Exported;

/**
* Responsible for caching {@link Project} instances so that they do not need to be re-built or re-generated for
* multiple consumers.
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
@Exported
public interface ProjectCache
{
/**
* Retrieve a {@link Project} from the cache, using {@link Project#getProjectRoot()} as the key.
*/
Project get(DirectoryResource target);

/**
* Invalidate the cache, forcing all stored projects to be re-built.
*/
void invalidate();

/**
* Store the given {@link Project} into this cache.
*/
void store(Project project);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.forge.addon.projects.impl;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.inject.Singleton;

import org.jboss.forge.addon.projects.Project;
import org.jboss.forge.addon.projects.spi.ProjectCache;
import org.jboss.forge.addon.resource.DirectoryResource;

/**
* A simple in-memory {@link ProjectCache}.
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
@Singleton
public class InMemoryProjectCache implements ProjectCache
{
private Map<String, Project> projects = new ConcurrentHashMap<String, Project>();

@Override
public Project get(DirectoryResource dir)
{
return projects.get(dir.getFullyQualifiedName());
}

@Override
public void invalidate()
{
this.projects.clear();
}

@Override
public void store(Project project)
{
this.projects.put(project.getProjectRoot().getFullyQualifiedName(), project);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;

Expand All @@ -16,12 +17,14 @@

import org.jboss.forge.addon.facets.Facet;
import org.jboss.forge.addon.facets.FacetFactory;
import org.jboss.forge.addon.facets.FacetNotFoundException;
import org.jboss.forge.addon.projects.Project;
import org.jboss.forge.addon.projects.ProjectAssociationProvider;
import org.jboss.forge.addon.projects.ProjectFacet;
import org.jboss.forge.addon.projects.ProjectFactory;
import org.jboss.forge.addon.projects.ProjectListener;
import org.jboss.forge.addon.projects.ProjectLocator;
import org.jboss.forge.addon.projects.spi.ProjectCache;
import org.jboss.forge.addon.resource.DirectoryResource;
import org.jboss.forge.addon.resource.FileResource;
import org.jboss.forge.addon.resource.ResourceFactory;
Expand Down Expand Up @@ -51,6 +54,9 @@ public class ProjectFactoryImpl implements ProjectFactory
@Inject
private Imported<ProjectListener> builtInListeners;

@Inject
private Imported<ProjectCache> caches;

private final List<ProjectListener> projectListeners = new ArrayList<ProjectListener>();

@Override
Expand Down Expand Up @@ -78,31 +84,61 @@ public boolean accept(Project type)
Imported<ProjectLocator> instances = registry.getServices(ProjectLocator.class);
for (ProjectLocator locator : instances)
{
DirectoryResource r = (target instanceof DirectoryResource) ? (DirectoryResource) target : target.getParent();
while (r != null && result == null)
try
{
if (locator.containsProject(r))
DirectoryResource r = (target instanceof DirectoryResource) ? (DirectoryResource) target : target
.getParent();
while (r != null && result == null)
{
result = locator.createProject(r);
if (!filter.accept(result))
result = null;
}
Iterator<ProjectCache> cacheIterator = caches.iterator();
while (cacheIterator.hasNext() && result == null)
{
ProjectCache cache = cacheIterator.next();
try
{
result = cache.get(r);
if (result != null && !filter.accept(result))
result = null;
if (result != null)
break;
}
finally
{
caches.release(cache);
}
}

r = r.getParent();
}
instances.release(locator);
}
if (result == null && locator.containsProject(r))
{
result = locator.createProject(r);
if (result != null && !filter.accept(result))
result = null;

if (result != null)
{
for (Class<ProjectFacet> instance : registry.getExportedTypes(ProjectFacet.class))
{
ProjectFacet facet = factory.create(result, instance);
if (facet != null && factory.register(result, facet))
{
log.fine("Registered Facet [" + facet + "] into Project [" + result + "]");
if (result != null)
{
for (Class<ProjectFacet> instance : registry.getExportedTypes(ProjectFacet.class))
{
ProjectFacet facet = factory.create(result, instance);
if (facet != null && factory.register(result, facet))
{
log.fine("Registered Facet [" + facet + "] into Project [" + result + "]");
}
}

cacheProject(result);
}
}

r = r.getParent();
}
}
finally
{
instances.release(locator);
}

if (result != null)
break;
}

return result;
Expand Down Expand Up @@ -174,11 +210,31 @@ public Project createProject(DirectoryResource target, Iterable<Class<? extends
}

if (result != null)
{
cacheProject(result);
fireProjectCreated(result);
}

return result;
}

private void cacheProject(Project result)
{
Iterator<ProjectCache> cacheIterator = caches.iterator();
while (cacheIterator.hasNext())
{
ProjectCache cache = cacheIterator.next();
try
{
cache.store(result);
}
finally
{
caches.release(cache);
}
}
}

private void fireProjectCreated(Project project)
{
for (ProjectListener listener : builtInListeners)
Expand Down Expand Up @@ -237,4 +293,22 @@ public boolean containsProject(FileResource<?> target)
}
return result;
}

@Override
public void invalidateCaches()
{
Iterator<ProjectCache> cacheIterator = caches.iterator();
while (cacheIterator.hasNext())
{
ProjectCache cache = cacheIterator.next();
try
{
cache.invalidate();
}
finally
{
caches.release(cache);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.jboss.forge.furnace.util.Predicate;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package org.jboss.forge.addon.projects.impl.cache;

/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/

import javax.inject.Inject;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.forge.addon.projects.Project;
import org.jboss.forge.addon.projects.ProjectFactory;
import org.jboss.forge.addon.projects.impl.MockProjectListener;
import org.jboss.forge.addon.projects.spi.ProjectCache;
import org.jboss.forge.arquillian.AddonDependency;
import org.jboss.forge.arquillian.Dependencies;
import org.jboss.forge.arquillian.archive.ForgeArchive;
import org.jboss.forge.furnace.repositories.AddonDependencyEntry;
import org.jboss.forge.furnace.services.Imported;
import org.jboss.forge.furnace.util.Predicate;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class ProjectFactoryCacheTest
{
@Deployment
@Dependencies({
@AddonDependency(name = "org.jboss.forge.addon:resources"),
@AddonDependency(name = "org.jboss.forge.addon:projects"),
@AddonDependency(name = "org.jboss.forge.addon:ui"),
@AddonDependency(name = "org.jboss.forge.addon:maven")
})
public static ForgeArchive getDeployment()
{
ForgeArchive archive = ShrinkWrap
.create(ForgeArchive.class)
.addClass(MockProjectListener.class)
.addBeansXML()
.addAsAddonDependencies(
AddonDependencyEntry.create("org.jboss.forge.furnace.container:cdi"),
AddonDependencyEntry.create("org.jboss.forge.addon:projects")
);

return archive;
}

@Inject
private Imported<ProjectCache> caches;

@Inject
private ProjectFactory projectFactory;

@Test
public void testInjectionNotNull()
{
Assert.assertNotNull(caches);
}

@Test
public void testFindProjectFromCache() throws Exception
{
Project project = projectFactory.createTempProject();

Assert.assertNotNull(project);
Project found = projectFactory.findProject(project.getProjectRoot());
Assert.assertNotNull(found);
Assert.assertSame(project, found);

Assert.assertNull(projectFactory.findProject(project.getProjectRoot(), new Predicate<Project>()
{
@Override
public boolean accept(Project type)
{
return false;
}
}));

Project found2 = projectFactory.findProject(project.getProjectRoot().getChildDirectory("src/main/java"));
Assert.assertNotNull(found2);
Assert.assertSame(found, found2);

Project project2 = projectFactory.createTempProject();
Assert.assertNotSame(found2, project2);
Assert.assertNotEquals(found2.getProjectRoot().getFullyQualifiedName(), project2.getProjectRoot()
.getFullyQualifiedName());

project.getProjectRoot().delete(true);
}

}

0 comments on commit 1f55c8e

Please sign in to comment.