diff --git a/doc/changelog.md b/doc/changelog.md index 8fa185fe5..a399eec36 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -4,6 +4,7 @@ - Check also registry stored with an `https` prefix (#367) - Add support for Docker network and `host`, `bridge` and `container` network modes (#335) - Add support for older Maven versions, minimum required version is now 3.0.5 (#290) + - Don't stop containers not started by the project during parallel reactor builds (#372) * **0.13.8** - Add option `nocache` to build configuration ([#348](https://github.com/rhuss/docker-maven-plugin/issues/348)) diff --git a/src/main/java/org/jolokia/docker/maven/StartMojo.java b/src/main/java/org/jolokia/docker/maven/StartMojo.java index 67d2d6912..ac778f228 100644 --- a/src/main/java/org/jolokia/docker/maven/StartMojo.java +++ b/src/main/java/org/jolokia/docker/maven/StartMojo.java @@ -63,6 +63,7 @@ public synchronized void executeInternal(final ServiceHub hub) throws DockerAcce PortMapping.PropertyWriteHelper portMappingPropertyWriteHelper = new PortMapping.PropertyWriteHelper(portPropertyFile); boolean success = false; + PomLabel pomLabel = getPomLabel(); try { for (StartOrderResolver.Resolvable resolvable : runService.getImagesConfigsInOrder(queryService, getImages())) { final ImageConfiguration imageConfig = (ImageConfiguration) resolvable; @@ -77,7 +78,7 @@ public synchronized void executeInternal(final ServiceHub hub) throws DockerAcce RunImageConfiguration runConfig = imageConfig.getRunConfiguration(); PortMapping portMapping = runService.getPortMapping(runConfig, projProperties); - String containerId = runService.createAndStartContainer(imageConfig, portMapping, getPomLabel(), projProperties); + String containerId = runService.createAndStartContainer(imageConfig, portMapping, pomLabel, projProperties); if (showLogs(imageConfig)) { dispatcher.trackContainerLog(containerId, @@ -109,7 +110,7 @@ public synchronized void executeInternal(final ServiceHub hub) throws DockerAcce } finally { if (!success) { log.error("Error occurred during container startup, shutting down..."); - runService.stopStartedContainers(keepContainer, removeVolumes); + runService.stopStartedContainers(keepContainer, removeVolumes, pomLabel); } } } diff --git a/src/main/java/org/jolokia/docker/maven/StopMojo.java b/src/main/java/org/jolokia/docker/maven/StopMojo.java index 948ccecd2..e7b66313e 100644 --- a/src/main/java/org/jolokia/docker/maven/StopMojo.java +++ b/src/main/java/org/jolokia/docker/maven/StopMojo.java @@ -39,12 +39,14 @@ public class StopMojo extends AbstractDockerMojo { protected void executeInternal(ServiceHub hub) throws MojoExecutionException, DockerAccessException { QueryService queryService = hub.getQueryService(); RunService runService = hub.getRunService(); + + PomLabel pomLabel = getPomLabel(); if (!keepRunning) { if (invokedTogetherWithDockerStart()) { - runService.stopStartedContainers(keepContainer, removeVolumes); + runService.stopStartedContainers(keepContainer, removeVolumes, pomLabel); } else { - stopContainers(queryService, runService); + stopContainers(queryService, runService, pomLabel); } } @@ -53,9 +55,7 @@ protected void executeInternal(ServiceHub hub) throws MojoExecutionException, Do dispatcher.untrackAllContainerLogs(); } - private void stopContainers(QueryService queryService, RunService runService) throws DockerAccessException { - PomLabel pomLabel = getPomLabel(); - + private void stopContainers(QueryService queryService, RunService runService, PomLabel pomLabel) throws DockerAccessException { for (ImageConfiguration image : getImages()) { for (Container container : getContainersToStop(queryService, image)) { if (shouldStopContainer(container, pomLabel)) { @@ -86,7 +86,7 @@ private boolean shouldStopContainer(Container container, PomLabel pomLabel) { String key = pomLabel.getKey(); Map labels = container.getLabels(); - return labels.containsKey(key) && pomLabel.matches(new PomLabel(labels.get(key))); + return labels.containsKey(key) && pomLabel.equals(new PomLabel(labels.get(key))); } private boolean isStopAllContainers() { diff --git a/src/main/java/org/jolokia/docker/maven/WatchMojo.java b/src/main/java/org/jolokia/docker/maven/WatchMojo.java index 1b7c315af..2b0efd8c0 100644 --- a/src/main/java/org/jolokia/docker/maven/WatchMojo.java +++ b/src/main/java/org/jolokia/docker/maven/WatchMojo.java @@ -230,7 +230,7 @@ private void restartContainer(ServiceHub hub, ImageWatcher watcher) throws Docke if (optionalPreStop != null) { runService.execInContainer(id, optionalPreStop, watcher.getImageConfiguration()); } - runService.stopContainer(id, false, false); + runService.stopPreviouslyStartedContainer(id, false, false); // Start new one watcher.setContainerId(runService.createAndStartContainer(imageConfig, mappedPorts, getPomLabel(), project.getProperties())); diff --git a/src/main/java/org/jolokia/docker/maven/service/ContainerTracker.java b/src/main/java/org/jolokia/docker/maven/service/ContainerTracker.java index d8ce76fad..2f86bb5dc 100644 --- a/src/main/java/org/jolokia/docker/maven/service/ContainerTracker.java +++ b/src/main/java/org/jolokia/docker/maven/service/ContainerTracker.java @@ -1,15 +1,14 @@ package org.jolokia.docker.maven.service; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.jolokia.docker.maven.config.*; +import org.jolokia.docker.maven.util.PomLabel; +/** + * Tracker class for tracking started containers so that they can be shut down at the end when + * docker:start and docker:stop are used in the same run + */ public class ContainerTracker { // Map holding associations between started containers and their images via name and aliases @@ -19,46 +18,124 @@ public class ContainerTracker { // Key: Alias, Value: container private final Map aliasToContainerMap = new HashMap<>(); - // Action to be used when doing a shutdown - private final Map shutdownDescriptorMap = new LinkedHashMap<>(); + // Maps holding actions to be used when doing a shutdown + private final Map shutdownDescriptorPerContainerMap = new LinkedHashMap<>(); + private final Map> shutdownDescriptorPerPomLabelMap = new HashMap<>(); /** - * Register a container to this tracker + * Register a started container to this tracker * * @param containerId container id to register * @param imageConfig configuration of associated image + * @param pomLabel pom label to identifying the reactor project where the container was created */ - public void registerContainer(String containerId, ImageConfiguration imageConfig) { - shutdownDescriptorMap.put(containerId, new ContainerShutdownDescriptor(imageConfig, containerId)); + public synchronized void registerContainer(String containerId, + ImageConfiguration imageConfig, + PomLabel pomLabel) { + ContainerShutdownDescriptor descriptor = new ContainerShutdownDescriptor(imageConfig, containerId); + shutdownDescriptorPerContainerMap.put(containerId, descriptor); + updatePomLabelMap(pomLabel, descriptor); updateImageToContainerMapping(imageConfig, containerId); } - - public ContainerShutdownDescriptor getContainerShutdownDescriptor(String containerId) { - return shutdownDescriptorMap.get(containerId); - } - - public ContainerShutdownDescriptor removeContainerShutdownDescriptor(String containerId) { - return shutdownDescriptorMap.remove(containerId); + + /** + * Remove a container from this container (if stored) and return its descriptor + * + * @param containerId id to remove + * @return descriptor of the container removed or null + */ + public synchronized ContainerShutdownDescriptor removeContainer(String containerId) { + ContainerShutdownDescriptor descriptor = shutdownDescriptorPerContainerMap.remove(containerId); + if (descriptor != null) { + removeContainerIdFromLookupMaps(containerId); + removeDescriptorFromPomLabelMap(descriptor); + } + return descriptor; } - - public String lookupContainer(String lookup) { + + /** + * Lookup a container by name or alias from the tracked containers + * + * @param lookup name or alias of the container to lookup + * @return container id found or null + */ + public synchronized String lookupContainer(String lookup) { if (aliasToContainerMap.containsKey(lookup)) { return aliasToContainerMap.get(lookup); } return imageToContainerMap.get(lookup); } - public void resetContainers() { - shutdownDescriptorMap.clear(); - } - - public Collection getAllContainerShutdownDescriptors() { - List descriptors = new ArrayList<>(shutdownDescriptorMap.values()); - Collections.reverse(descriptors); + /** + * Get all shutdown descriptors for a given pom label and remove it from the tracker. The descriptors + * are returned in reverse order of their registration. + * + * If no pom label is given, then all descriptors are returned. + * + * @param pomLabel the label for which to get the descriptors or null for all descriptors + * @return the descriptors for the given label or an empty collection + */ + public synchronized Collection removeShutdownDescriptors(PomLabel pomLabel) { + List descriptors; + if (pomLabel != null) { + descriptors = removeFromPomLabelMap(pomLabel); + removeFromPerContainerMap(descriptors); + } else { + // All entries are requested + descriptors = new ArrayList<>(shutdownDescriptorPerContainerMap.values()); + clearAllMaps(); + } + Collections.reverse(descriptors); return descriptors; } - + + // ======================================================== + + private void updatePomLabelMap(PomLabel pomLabel, ContainerShutdownDescriptor descriptor) { + if (pomLabel != null) { + List descList = shutdownDescriptorPerPomLabelMap.get(pomLabel); + if (descList == null) { + descList = new ArrayList<>(); + shutdownDescriptorPerPomLabelMap.put(pomLabel,descList); + } + descList.add(descriptor); + } + } + + private void removeDescriptorFromPomLabelMap(ContainerShutdownDescriptor descriptor) { + Iterator>> mapIt = shutdownDescriptorPerPomLabelMap.entrySet().iterator(); + while(mapIt.hasNext()) { + Map.Entry> mapEntry = mapIt.next(); + List descs = mapEntry.getValue(); + Iterator it = descs.iterator(); + while (it.hasNext()) { + ContainerShutdownDescriptor desc = it.next(); + if (descriptor.equals(desc)) { + it.remove(); + } + } + if (descs.size() == 0) { + mapIt.remove(); + } + } + } + + private void removeContainerIdFromLookupMaps(String containerId) { + removeValueFromMap(imageToContainerMap,containerId); + removeValueFromMap(aliasToContainerMap,containerId); + } + + private void removeValueFromMap(Map map, String value) { + Iterator> it = map.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (entry.getValue().equals(value)) { + it.remove(); + } + } + } + private void updateImageToContainerMapping(ImageConfiguration imageConfig, String id) { // Register name -> containerId and alias -> name imageToContainerMap.put(imageConfig.getName(), id); @@ -67,6 +144,32 @@ private void updateImageToContainerMapping(ImageConfiguration imageConfig, Strin } } + private void removeFromPerContainerMap(List descriptors) { + Iterator> it = shutdownDescriptorPerContainerMap.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (descriptors.contains(entry.getValue())) { + removeContainerIdFromLookupMaps(entry.getKey()); + it.remove(); + } + } + } + + private List removeFromPomLabelMap(PomLabel pomLabel) { + List descriptors; + descriptors = shutdownDescriptorPerPomLabelMap.remove(pomLabel); + if (descriptors == null) { + descriptors = new ArrayList<>(); + } return descriptors; + } + + private void clearAllMaps() { + shutdownDescriptorPerContainerMap.clear(); + shutdownDescriptorPerPomLabelMap.clear(); + imageToContainerMap.clear(); + aliasToContainerMap.clear(); + } + // ======================================================= static class ContainerShutdownDescriptor { @@ -78,10 +181,10 @@ static class ContainerShutdownDescriptor { private final String containerId; // How long to wait after shutdown (in milliseconds) - private int shutdownGracePeriod; + private final int shutdownGracePeriod; // How long to wait after stop to kill container (in seconds) - private int killGracePeriod; + private final int killGracePeriod; // Command to call before stopping container private String preStop; @@ -89,6 +192,7 @@ static class ContainerShutdownDescriptor { ContainerShutdownDescriptor(ImageConfiguration imageConfig, String containerId) { this.imageConfig = imageConfig; this.containerId = containerId; + RunImageConfiguration runConfig = imageConfig.getRunConfiguration(); WaitConfiguration waitConfig = runConfig != null ? runConfig.getWaitConfiguration() : null; this.shutdownGracePeriod = waitConfig != null ? waitConfig.getShutdown() : 0; diff --git a/src/main/java/org/jolokia/docker/maven/service/RunService.java b/src/main/java/org/jolokia/docker/maven/service/RunService.java index 5c2b3514e..0ff7a1eec 100644 --- a/src/main/java/org/jolokia/docker/maven/service/RunService.java +++ b/src/main/java/org/jolokia/docker/maven/service/RunService.java @@ -80,6 +80,7 @@ public String execInContainer(String containerId, String command, ImageConfigura * @param imageConfig image configuration holding the run information and the image name * @param mappedPorts container port mapping * @param mavenProps properties to fill in with dynamically assigned ports + * @param pomLabel label to tag the started container with * * @return the container id * @@ -95,7 +96,7 @@ public String createAndStartContainer(ImageConfiguration imageConfig, ContainerCreateConfig config = createContainerConfig(imageName, runConfig, mappedPorts, pomLabel, mavenProps); String id = docker.createContainer(config, containerName); - startContainer(imageConfig, id); + startContainer(imageConfig, id, pomLabel); if (mappedPorts.containsDynamicPorts() || mappedPorts.containsDynamicHostIps()) { updateMappedPorts(id, mappedPorts); @@ -123,22 +124,20 @@ public void stopContainer(String containerId, /** * Lookup up whether a certain has been already started and registered. If so, stop it + * * @param containerId the container to stop * @param keepContainer whether to keep container or to remove them after stoppings * @param removeVolumes whether to remove volumes after stopping * * @throws DockerAccessException */ - public void stopContainer(String containerId, - boolean keepContainer, - boolean removeVolumes) + public void stopPreviouslyStartedContainer(String containerId, + boolean keepContainer, + boolean removeVolumes) throws DockerAccessException { - synchronized (tracker) { - ContainerTracker.ContainerShutdownDescriptor descriptor = tracker.getContainerShutdownDescriptor(containerId); - if (descriptor != null) { - shutdown(docker, descriptor, log, keepContainer, removeVolumes); - tracker.removeContainerShutdownDescriptor(containerId); - } + ContainerTracker.ContainerShutdownDescriptor descriptor = tracker.removeContainer(containerId); + if (descriptor != null) { + shutdown(docker, descriptor, log, keepContainer, removeVolumes); } } @@ -150,13 +149,11 @@ public void stopContainer(String containerId, * @throws DockerAccessException if during stopping of a container sth fails */ public void stopStartedContainers(boolean keepContainer, - boolean removeVolumes) + boolean removeVolumes, + PomLabel pomLabel) throws DockerAccessException { - synchronized (tracker) { - for (ContainerTracker.ContainerShutdownDescriptor descriptor : tracker.getAllContainerShutdownDescriptors()) { - shutdown(docker, descriptor, log, keepContainer, removeVolumes); - } - tracker.resetContainers(); + for (ContainerTracker.ContainerShutdownDescriptor descriptor : tracker.removeShutdownDescriptors(pomLabel)) { + shutdown(docker, descriptor, log, keepContainer, removeVolumes); } } @@ -202,7 +199,7 @@ public void addShutdownHookForStoppingContainers(final boolean keepContainer, fi @Override public void run() { try { - stopStartedContainers(keepContainer, removeVolumes); + stopStartedContainers(keepContainer, removeVolumes, null); } catch (DockerAccessException e) { log.error("Error while stopping containers: " + e); } @@ -360,10 +357,10 @@ private String findContainerId(String imageNameOrAlias, boolean checkAllContaine return id; } - private void startContainer(ImageConfiguration imageConfig, String id) throws DockerAccessException { + private void startContainer(ImageConfiguration imageConfig, String id, PomLabel pomLabel) throws DockerAccessException { log.info(imageConfig.getDescription() + ": Start container " + id); docker.startContainer(id); - tracker.registerContainer(id, imageConfig); + tracker.registerContainer(id, imageConfig, pomLabel); } private void updateMappedPorts(String containerId, PortMapping mappedPorts) throws DockerAccessException { @@ -409,6 +406,7 @@ private void shutdown(DockerAccess access, // Remove the container access.removeContainer(containerId, removeVolumes); } + log.info(descriptor.getDescription() + ": Stop" + (keepContainer ? "" : " and remove") + " container " + containerId.substring(0, 12)); } diff --git a/src/main/java/org/jolokia/docker/maven/util/PomLabel.java b/src/main/java/org/jolokia/docker/maven/util/PomLabel.java index 90a12aae7..a3b60f9c0 100644 --- a/src/main/java/org/jolokia/docker/maven/util/PomLabel.java +++ b/src/main/java/org/jolokia/docker/maven/util/PomLabel.java @@ -69,17 +69,20 @@ public String getValue() { return mavenCoordinates; } - /** - * Check whether this label matches another. - *

- * To match, the maven coordinates must be equal and if incRunId is true, the runId must also - * match. - *

- * - * @param other label to match - * @return true for a match - */ - public boolean matches(PomLabel other) { - return other.mavenCoordinates.equals(mavenCoordinates); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + return mavenCoordinates.equals(((PomLabel) o).mavenCoordinates); + } + + @Override + public int hashCode() { + return mavenCoordinates.hashCode(); } } diff --git a/src/test/java/org/jolokia/docker/maven/service/ContainerTrackerTest.java b/src/test/java/org/jolokia/docker/maven/service/ContainerTrackerTest.java new file mode 100644 index 000000000..13a2f3c64 --- /dev/null +++ b/src/test/java/org/jolokia/docker/maven/service/ContainerTrackerTest.java @@ -0,0 +1,175 @@ +package org.jolokia.docker.maven.service; +/* + * + * Copyright 2016 Roland Huss + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.*; + +import org.jolokia.docker.maven.config.ImageConfiguration; +import org.jolokia.docker.maven.config.RunImageConfiguration; +import org.jolokia.docker.maven.config.WaitConfiguration; +import org.jolokia.docker.maven.util.PomLabel; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author roland + * @since 14/02/16 + */ +public class ContainerTrackerTest { + + private ContainerTracker tracker; + + @Before + public void setUp() throws Exception { + tracker = new ContainerTracker(); + } + + @Test + public void lookup() throws Exception { + tracker.registerContainer("1234",getImageConfiguration("name","alias"),getPomLabel("test1")); + + assertEquals("1234",tracker.lookupContainer("name")); + assertEquals("1234",tracker.lookupContainer("alias")); + assertNull(tracker.lookupContainer("blub")); + } + + @Test + public void removeContainer() throws Exception { + String data[][] = new String[][] { + {"1", "name1", "alias1", "100", "200", "stop1", "label1"}, + {"2", "name2", "alias2", null, null, null, "label2"} + }; + + List descs = registerAtTracker(data); + + ContainerTracker.ContainerShutdownDescriptor desc = tracker.removeContainer("1"); + verifyDescriptor(data[0],desc); + assertNull(tracker.lookupContainer("1")); + assertNull(tracker.removeContainer("1")); + + assertTrue(tracker.removeShutdownDescriptors(getPomLabel("label1")).isEmpty()); + assertFalse(tracker.removeShutdownDescriptors(getPomLabel("label2")).isEmpty()); + assertTrue(tracker.removeShutdownDescriptors(getPomLabel("label2")).isEmpty()); + } + + @Test + public void removeDescriptors() throws Exception { + + String data[][] = new String[][] { + { "1", "name1", "alias1", "100", "200", "stop1", "label1" }, + { "2", "name2", "alias2", null, null, null, "label1" }, + { "3", "name3", null, null, null, null, "label2" } + }; + + List descs = registerAtTracker(data); + + Collection removed = tracker.removeShutdownDescriptors(getPomLabel("label1")); + assertEquals(2,removed.size()); + Iterator it = removed.iterator(); + // Reverse order + verifyDescriptor(data[1],it.next()); + verifyDescriptor(data[0],it.next()); + + assertNull(tracker.lookupContainer("name1")); + assertNull(tracker.lookupContainer("alias1")); + assertNull(tracker.lookupContainer("name2")); + assertNull(tracker.lookupContainer("alias2")); + assertNotNull(tracker.lookupContainer("name3")); + } + + @Test + public void removeAll() throws Exception { + String data[][] = new String[][] { + { "1", "name1", "alias1", "100", "200", "stop1", "label1" }, + { "2", "name2", "alias2", null, null, null, "label1" }, + { "3", "name3", null, null, null, null, "label2" } + }; + + List descs = registerAtTracker(data); + Collection removed = tracker.removeShutdownDescriptors(null); + + assertEquals(3,removed.size()); + Iterator it = removed.iterator(); + // Reverse order + verifyDescriptor(data[2],it.next()); + verifyDescriptor(data[1],it.next()); + verifyDescriptor(data[0],it.next()); + + assertEquals(0,tracker.removeShutdownDescriptors(null).size()); + } + + private void verifyDescriptor(String[] d, ContainerTracker.ContainerShutdownDescriptor desc) { + assertNotNull(desc); + assertEquals(desc.getContainerId(),d[0]); + assertEquals(desc.getImage(),d[1]); + if (d[2] != null) { + assertTrue(desc.getDescription().contains(d[2])); + } + assertEquals(desc.getShutdownGracePeriod(),parseInt(d[3])); + assertEquals(desc.getKillGracePeriod(),parseInt(d[4])); + assertEquals(desc.getPreStop(),d[5]); + assertNotNull(desc.getImageConfiguration()); + } + + private List registerAtTracker(String[][] data) { + List descriptors = new ArrayList<>(); + for (String[] d : data) { + ImageConfiguration imageConfig = + getImageConfiguration(d[1], d[2], parseInt(d[3]), parseInt(d[4]), d[5]); + + tracker.registerContainer(d[0], + imageConfig, + getPomLabel(d[6])); + descriptors.add(new ContainerTracker.ContainerShutdownDescriptor(imageConfig, d[0])); + } + return descriptors; + } + + private int parseInt(String s) { + return s != null ? Integer.parseInt(s) : 0; + } + + private PomLabel getPomLabel(String artifactId) { + return new PomLabel("org.jolokia",artifactId,"1.0.0"); + } + + private ImageConfiguration getImageConfiguration(String name, String alias) { + return getImageConfiguration(name, alias, 0,0,null); + } + + private ImageConfiguration getImageConfiguration(String name, String alias, int shutdown,int kill, String preStop) { + WaitConfiguration waitConfig = null; + if (shutdown != 0 && kill != 0) { + WaitConfiguration.Builder builder = new WaitConfiguration.Builder() + .shutdown(shutdown) + .kill(kill); + if (preStop != null) { + builder.preStop(preStop); + } + waitConfig = builder.build(); + } + return new ImageConfiguration.Builder() + .name(name) + .alias(alias) + .runConfig(new RunImageConfiguration.Builder() + .wait(waitConfig) + .build()) + .build(); + } +} diff --git a/src/test/java/org/jolokia/docker/maven/util/PomLabelTest.java b/src/test/java/org/jolokia/docker/maven/util/PomLabelTest.java index 35fb1ac2e..61744152d 100644 --- a/src/test/java/org/jolokia/docker/maven/util/PomLabelTest.java +++ b/src/test/java/org/jolokia/docker/maven/util/PomLabelTest.java @@ -40,13 +40,13 @@ public void simple() throws Exception { @Test public void dontMatch() { PomLabel label = new PomLabel(g, a, v); - assertFalse(label.matches(new PomLabel(g, a, "2.1.1"))); + assertFalse(label.equals(new PomLabel(g, a, "2.1.1"))); } @Test public void match() { PomLabel label = new PomLabel(g, a, v); - assertTrue(label.matches(new PomLabel(g, a, v))); + assertTrue(label.equals(new PomLabel(g, a, v))); } @Test