-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #349 from elandau/guice_admin
Admin pages for Guice
- Loading branch information
Showing
21 changed files
with
1,674 additions
and
34 deletions.
There are no files selected for viewing
231 changes: 231 additions & 0 deletions
231
karyon2-admin-web/src/main/java/com/google/inject/grapher/KaryonAbstractInjectorGrapher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
package com.google.inject.grapher; | ||
|
||
import com.google.common.collect.Lists; | ||
import com.google.common.collect.Maps; | ||
import com.google.common.collect.Sets; | ||
import com.google.inject.Binding; | ||
import com.google.inject.ConfigurationException; | ||
import com.google.inject.Injector; | ||
import com.google.inject.Key; | ||
|
||
import java.io.IOException; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
public abstract class KaryonAbstractInjectorGrapher implements InjectorGrapher { | ||
private final RootKeySetCreator rootKeySetCreator; | ||
private final AliasCreator aliasCreator; | ||
private final NodeCreator nodeCreator; | ||
private final EdgeCreator edgeCreator; | ||
|
||
/** | ||
* Parameters used to override default settings of the grapher. | ||
* | ||
* @since 4.0 | ||
*/ | ||
public static final class GrapherParameters { | ||
private RootKeySetCreator rootKeySetCreator = new DefaultRootKeySetCreator(); | ||
private AliasCreator aliasCreator = new ProviderAliasCreator(); | ||
private NodeCreator nodeCreator = new DefaultNodeCreator(); | ||
private EdgeCreator edgeCreator = new DefaultEdgeCreator(); | ||
|
||
public RootKeySetCreator getRootKeySetCreator() { | ||
return rootKeySetCreator; | ||
} | ||
|
||
public GrapherParameters setRootKeySetCreator( | ||
RootKeySetCreator rootKeySetCreator) { | ||
this.rootKeySetCreator = rootKeySetCreator; | ||
return this; | ||
} | ||
|
||
public AliasCreator getAliasCreator() { | ||
return aliasCreator; | ||
} | ||
|
||
public GrapherParameters setAliasCreator(AliasCreator aliasCreator) { | ||
this.aliasCreator = aliasCreator; | ||
return this; | ||
} | ||
|
||
public NodeCreator getNodeCreator() { | ||
return nodeCreator; | ||
} | ||
|
||
public GrapherParameters setNodeCreator(NodeCreator nodeCreator) { | ||
this.nodeCreator = nodeCreator; | ||
return this; | ||
} | ||
|
||
public EdgeCreator getEdgeCreator() { | ||
return edgeCreator; | ||
} | ||
|
||
public GrapherParameters setEdgeCreator(EdgeCreator edgeCreator) { | ||
this.edgeCreator = edgeCreator; | ||
return this; | ||
} | ||
} | ||
|
||
public KaryonAbstractInjectorGrapher() { | ||
this(new GrapherParameters()); | ||
} | ||
|
||
public KaryonAbstractInjectorGrapher(GrapherParameters options) { | ||
this.rootKeySetCreator = options.getRootKeySetCreator(); | ||
this.aliasCreator = options.getAliasCreator(); | ||
this.nodeCreator = options.getNodeCreator(); | ||
this.edgeCreator = options.getEdgeCreator(); | ||
} | ||
|
||
@Override | ||
public final void graph(Injector injector) throws IOException { | ||
graph(injector, rootKeySetCreator.getRootKeys(injector)); | ||
} | ||
|
||
@Override | ||
public final void graph(Injector injector, Set<Key<?>> root) | ||
throws IOException { | ||
reset(); | ||
|
||
Iterable<Binding<?>> bindings = getBindings(injector, root); | ||
Map<NodeId, NodeId> aliases = resolveAliases(aliasCreator | ||
.createAliases(bindings)); | ||
createNodes(nodeCreator.getNodes(bindings), aliases); | ||
createEdges(edgeCreator.getEdges(bindings), aliases); | ||
postProcess(); | ||
} | ||
|
||
/** Resets the state of the grapher before rendering a new graph. */ | ||
protected abstract void reset() throws IOException; | ||
|
||
/** Adds a new interface node to the graph. */ | ||
protected abstract void newInterfaceNode(InterfaceNode node) | ||
throws IOException; | ||
|
||
/** Adds a new implementation node to the graph. */ | ||
protected abstract void newImplementationNode(ImplementationNode node) | ||
throws IOException; | ||
|
||
/** Adds a new instance node to the graph. */ | ||
protected abstract void newInstanceNode(InstanceNode node) | ||
throws IOException; | ||
|
||
/** Adds a new dependency edge to the graph. */ | ||
protected abstract void newDependencyEdge(DependencyEdge edge) | ||
throws IOException; | ||
|
||
/** Adds a new binding edge to the graph. */ | ||
protected abstract void newBindingEdge(BindingEdge edge) throws IOException; | ||
|
||
/** | ||
* Performs any post processing required after all nodes and edges have been | ||
* added. | ||
*/ | ||
protected abstract void postProcess() throws IOException; | ||
|
||
private void createNodes(Iterable<Node> nodes, Map<NodeId, NodeId> aliases) | ||
throws IOException { | ||
for (Node node : nodes) { | ||
NodeId originalId = node.getId(); | ||
NodeId resolvedId = resolveAlias(aliases, originalId); | ||
node = node.copy(resolvedId); | ||
|
||
// Only render nodes that aren't aliased to some other node. | ||
if (resolvedId.equals(originalId)) { | ||
if (node instanceof InterfaceNode) { | ||
newInterfaceNode((InterfaceNode) node); | ||
} else if (node instanceof ImplementationNode) { | ||
newImplementationNode((ImplementationNode) node); | ||
} else { | ||
newInstanceNode((InstanceNode) node); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private void createEdges(Iterable<Edge> edges, Map<NodeId, NodeId> aliases) | ||
throws IOException { | ||
for (Edge edge : edges) { | ||
edge = edge.copy(resolveAlias(aliases, edge.getFromId()), | ||
resolveAlias(aliases, edge.getToId())); | ||
if (!edge.getFromId().equals(edge.getToId())) { | ||
if (edge instanceof BindingEdge) { | ||
newBindingEdge((BindingEdge) edge); | ||
} else { | ||
newDependencyEdge((DependencyEdge) edge); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private NodeId resolveAlias(Map<NodeId, NodeId> aliases, NodeId nodeId) { | ||
return aliases.containsKey(nodeId) ? aliases.get(nodeId) : nodeId; | ||
} | ||
|
||
/** | ||
* Transitively resolves aliases. Given aliases (X to Y) and (Y to Z), it | ||
* will return mappings (X to Z) and (Y to Z). | ||
*/ | ||
private Map<NodeId, NodeId> resolveAliases(Iterable<Alias> aliases) { | ||
Map<NodeId, NodeId> resolved = Maps.newHashMap(); | ||
Map<NodeId, Set<NodeId>> inverse = Maps.newHashMap(); | ||
|
||
for (Alias alias : aliases) { | ||
NodeId from = alias.getFromId(); | ||
NodeId to = alias.getToId(); | ||
if (resolved.containsKey(to)) { | ||
to = resolved.get(to); | ||
} | ||
resolved.put(from, to); | ||
if (inverse.get(to) == null) { | ||
inverse.put(to, Sets.<NodeId> newHashSet()); | ||
} | ||
inverse.get(to).add(from); | ||
|
||
Set<NodeId> prev = inverse.get(from); | ||
if (prev != null) { | ||
for (NodeId id : prev) { | ||
resolved.remove(id); | ||
inverse.get(from).remove(id); | ||
resolved.put(id, to); | ||
inverse.get(to).add(id); | ||
} | ||
} | ||
} | ||
|
||
return resolved; | ||
} | ||
|
||
/** | ||
* Returns the bindings for the root keys and their transitive dependencies. | ||
*/ | ||
private Iterable<Binding<?>> getBindings(Injector injector, Set<Key<?>> root) { | ||
Set<Key<?>> keys = Sets.newHashSet(root); | ||
Set<Key<?>> visitedKeys = Sets.newHashSet(); | ||
List<Binding<?>> bindings = Lists.newArrayList(); | ||
TransitiveDependencyVisitor keyVisitor = new TransitiveDependencyVisitor(); | ||
|
||
while (!keys.isEmpty()) { | ||
Iterator<Key<?>> iterator = keys.iterator(); | ||
Key<?> key = iterator.next(); | ||
iterator.remove(); | ||
|
||
if (!visitedKeys.contains(key)) { | ||
try { | ||
Binding<?> binding = injector.getBinding(key); | ||
bindings.add(binding); | ||
visitedKeys.add(key); | ||
keys.addAll(binding.acceptTargetVisitor(keyVisitor)); | ||
} | ||
catch (ConfigurationException e) { | ||
System.out.println("Missing binding for : " + key); | ||
visitedKeys.add(key); | ||
} | ||
} | ||
} | ||
return bindings; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
karyon2-admin-web/src/main/java/netflix/adminresources/pages/GuiceGraphPage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package netflix.adminresources.pages; | ||
|
||
import com.google.inject.Module; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import netflix.adminresources.AbstractAdminPageInfo; | ||
import netflix.adminresources.AdminPage; | ||
|
||
@AdminPage | ||
public class GuiceGraphPage extends AbstractAdminPageInfo { | ||
|
||
public static final String PAGE_ID = "guicegraph"; | ||
public static final String NAME = "GuiceGraph"; | ||
|
||
public GuiceGraphPage() { | ||
super(PAGE_ID, NAME); | ||
} | ||
|
||
@Override | ||
public List<Module> getGuiceModules() { | ||
return Arrays.<Module>asList(new KaryonGrapherModule()); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
karyon2-admin-web/src/main/java/netflix/adminresources/pages/GuicePage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package netflix.adminresources.pages; | ||
|
||
import com.google.inject.Module; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import netflix.adminresources.AbstractAdminPageInfo; | ||
import netflix.adminresources.AdminPage; | ||
|
||
@AdminPage | ||
public class GuicePage extends AbstractAdminPageInfo { | ||
|
||
public static final String PAGE_ID = "guice"; | ||
public static final String NAME = "Guice"; | ||
|
||
public GuicePage() { | ||
super(PAGE_ID, NAME); | ||
} | ||
|
||
@Override | ||
public List<Module> getGuiceModules() { | ||
return Arrays.<Module>asList(new KaryonGrapherModule()); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
karyon2-admin-web/src/main/java/netflix/adminresources/pages/GuiceProvisionPage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package netflix.adminresources.pages; | ||
|
||
import com.google.inject.Module; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import netflix.adminresources.AbstractAdminPageInfo; | ||
import netflix.adminresources.AdminPage; | ||
|
||
@AdminPage | ||
public class GuiceProvisionPage extends AbstractAdminPageInfo { | ||
|
||
public static final String PAGE_ID = "guiceprovision"; | ||
public static final String NAME = "GuiceProvision"; | ||
|
||
public GuiceProvisionPage() { | ||
super(PAGE_ID, NAME); | ||
} | ||
|
||
@Override | ||
public List<Module> getGuiceModules() { | ||
return Arrays.<Module>asList(new KaryonGrapherModule()); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
karyon2-admin-web/src/main/java/netflix/adminresources/pages/KaryonGrapherModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package netflix.adminresources.pages; | ||
|
||
import com.google.inject.AbstractModule; | ||
import com.google.inject.grapher.NameFactory; | ||
import com.google.inject.grapher.ShortNameFactory; | ||
import com.google.inject.grapher.graphviz.PortIdFactory; | ||
import com.google.inject.grapher.graphviz.PortIdFactoryImpl; | ||
|
||
public class KaryonGrapherModule extends AbstractModule { | ||
|
||
@Override | ||
protected void configure() { | ||
bind(NameFactory.class).to(ShortNameFactory.class); | ||
bind(PortIdFactory.class).to(PortIdFactoryImpl.class); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return getClass().hashCode(); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
return getClass().equals(obj.getClass()); | ||
} | ||
|
||
} |
Oops, something went wrong.