diff --git a/make/CreateJars.gmk b/make/CreateJars.gmk index e11ddc7bb5..f1628154f8 100644 --- a/make/CreateJars.gmk +++ b/make/CreateJars.gmk @@ -559,7 +559,8 @@ EXPORTED_PRIVATE_PKGS = com.oracle.net \ com.oracle.nio \ com.alibaba.jwarmup \ com.alibaba.management \ - com.alibaba.jvm.gc + com.alibaba.jvm.gc \ + com.alibaba.rcm $(IMAGES_OUTPUTDIR)/symbols/_the.symbols: $(IMAGES_OUTPUTDIR)/lib/rt.jar $(RM) -r $(IMAGES_OUTPUTDIR)/symbols/META-INF/sym diff --git a/src/share/classes/com/alibaba/rcm/Constraint.java b/src/share/classes/com/alibaba/rcm/Constraint.java new file mode 100644 index 0000000000..85dd7a0d14 --- /dev/null +++ b/src/share/classes/com/alibaba/rcm/Constraint.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +package com.alibaba.rcm; + +import java.util.Arrays; + +/** + * A {@code Constraint} is a pair of {@link ResourceType} and {@code values}. + * The constrained resource and specification of parameter values are documented + * in enum constants in {@link ResourceType} + *
+ * A {@code Constraint} must be associated with one {@code ResourceType}, so we + * provide factory method {@link ResourceType#newConstraint(long...)} to implement + * this restriction. For example: + *
+ * Constraint cpuConstraint = ResourceType.CPU_PERCENT.newConstraint(30); + *+ *
+ * {@code ResourceContainer} and {@code Constraint} follow the one-to-many relationship. + * {@link ResourceContainer#getConstraints()} can fetch the Constraint associated with + * ResourceContainer. + */ +public class Constraint { + private final ResourceType type; + private final long[] values; + + /** + * Constraint should be instantiated by {@link ResourceType#newConstraint(long...)} + */ + Constraint(ResourceType type, long[] values) { + assert type.validate(values); + this.type = type; + this.values = values; + } + + /** + * Returns the currently restricted resource type. + * + * @return resource type + */ + public ResourceType getResourceType() { + return type; + } + + /** + * Returns the constraint value of ResourceType described by a long[], + * which is documented on the ResourceType enums. + *
+ * The returned value is a copy of the internal storage array to prevent + * modification. + * + * @return constraint values + */ + public long[] getValues() { + return Arrays.copyOf(values, values.length); + } + + @Override + public String toString() { + return "Constraint{" + + "type=" + type + + ", values=" + Arrays.toString(values) + + '}'; + } +} diff --git a/src/share/classes/com/alibaba/rcm/ResourceContainer.java b/src/share/classes/com/alibaba/rcm/ResourceContainer.java new file mode 100644 index 0000000000..d8f639b2f5 --- /dev/null +++ b/src/share/classes/com/alibaba/rcm/ResourceContainer.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +package com.alibaba.rcm; + +import com.alibaba.rcm.internal.AbstractResourceContainer; + +/** + * A {@code ResourceContainer} defines a set of resource {@link Constraint}s + * that limit resource usage by threads. + * Zero or more threads can be attached to one {@code ResourceContainer}. + *
+ * +-------------------------------+ +-----------------------------+ + * |ResourceContainer | |RootContainer | + * | | | | + * | +---------------+ | | | + * | |CPU Constraint | | | +----------+ | + * | +---------------+ <------------------+ Thread | | + * | +---------------+ | | +----------+ | + * | |Mem Constraint |command.run()| | container.run(command) | + * | +---------------+ | | | | + * | |----------------> v | | | + * | resources are command.run()| | | + * | controlled by returns | | back to root | + * | constraints +-----------------> container | + * | | | | + * | | | | + * +-------------------------------+ +-----------------------------+ + *+ * The figure above describes the structure and usage of {@code ResourceContainer}. + *
+ * The main Thread is bounded to root container which can be fetched by + * {@link ResourceContainer#root()}. Root Container is the system default + * {@code ResourceContainer} without any resource restrictions. + * Newly created threads are implicitly bounded to {@code ResourceContainer} + * of the parent thread. + *
+ * A Thread can invoke {@link ResourceContainer#run(Runnable command)} to + * attach to the {@code ResourceContainer} and run the {@code command}, + * the resource usage of the thread is controlled by the {@code ResourceContainer}'s + * constraints while running the command. + * When the execution of the command is either finished normally + * or terminated by Exception, the thread will be detached from the container automatically. + *
+ * Components of a {@code ResourceContainer} implementation: + *
+ * {@code ResourceContainer} needs to be created from a set of {@code Constraint}s + *
+ * In most cases, the following idiom should be used: + *
+ * ResourceContainer resourceContainer = containerFactory.createContainer( + * Arrays.asList( + * CPU_PERCENT.newConstraint(50), + * HEAP_RETAINED.newConstraint(100_000_000) + * )); + * + * resourceContainer.run(requestHandler); + * + * resourceContainer.destroy(); + *+ * + * @see ResourceContainerFactory + */ +public interface ResourceContainer { + /** + * An enumeration of Container state + */ + enum State { + /** + * Created but not ready for attaching (initializing). + */ + CREATED, + /** + * Ready for attaching. + */ + RUNNING, + /** + * {@link ResourceContainer#destroy()} has been called. + */ + STOPPING, + /** + * Container is destroyed. Further usage is not allowed. + */ + DEAD + } + + /** + * Returns the system-wide "root" Resource container. + *
+ * Root ResourceContainer is a virtual container that indicates
+ * the default resource container for any thread, which is not attached
+ * to a ResourceContainer created by users. Root ResourceContainer does
+ * not have any resource constrains.
+ *
+ * {@link #run(Runnable)} method of root container is a special
+ * implementation that detaches from the current container and returns
+ * to the root container.
+ * It is very useful in ResourceContainer switch scenario:
+ *
+ * At the same time, it is not allowed to switch directly between any two
+ * containers. If the switch is indeed required, the
+ * {@link #root()} container should be used.
+ *
+ * This way restricts the container attach/detach mode for the API users,
+ * but is less error-prone.
+ *
+ *
+ * Constraints with an identical type will
+ * replace each other according to the calling order.
+ *
+ * @param constraint constraints list
+ * @throws UnsupportedOperationException {@link Constraint#getResourceType()} is not
+ * supported by the implementation
+ */
+ void updateConstraint(Constraint constraint);
+
+ /**
+ * Gets container's {@link Constraint}s
+ *
+ * @return {@code Constraint}s
+ */
+ Iterable
+ * Once this method is called, the state will become {@link State#STOPPING}.
+ * And the caller thread will be blocked until all the resources have been released.
+ * Then the container state will become {@link State#DEAD}.
+ */
+ void destroy();
+}
diff --git a/src/share/classes/com/alibaba/rcm/ResourceContainerFactory.java b/src/share/classes/com/alibaba/rcm/ResourceContainerFactory.java
new file mode 100644
index 0000000000..ceadfbcd6a
--- /dev/null
+++ b/src/share/classes/com/alibaba/rcm/ResourceContainerFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Alibaba designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+package com.alibaba.rcm;
+
+/**
+ * Factory class for {@link ResourceContainer}.
+ *
+ * Each ResourceContainer implementation needs to provide a public ResourceContainerFactory
+ * instance to allow users to choose a specific ResourceContainer implementation:
+ *
+ * {@code class} is used instead of {@code enum} to provide extensibility.
+ * Implementation of resource management can define its resource type.
+ * Below is an example of extension ResourceType:
+ *
+ *
+ * CPU_CFS is an instance of {@code ResourceType}, so it can be used wherever
+ * ResourceType is handled.
+ *
+ * The descriptions and parameters of each public final static value need to
+ * be documented in detail.
+ */
+public class ResourceType {
+ /**
+ * Throttling the CPU usage by CPU percentage.
+ *
+ * param #1: CPU usage measured in a percentage granularity.
+ * The value ranges from 0 to CPU_COUNT * 100. For example, {@code 150}
+ * means that ResourceContainer can use up to 1.5 CPU cores.
+ */
+ public final static ResourceType CPU_PERCENT =
+ new ResourceType("CPU_PERCENT", newChecker(0, Runtime.getRuntime().availableProcessors() * 100));
+ /**
+ * Throttling the max heap usage.
+ *
+ * param #1: maximum heap size in bytes
+ */
+ public final static ResourceType HEAP_RETAINED =
+ new ResourceType("HEAP_RETAINED", newChecker(0, Runtime.getRuntime().maxMemory()));
+
+ private final String name;
+ private final ParameterChecker[] checkers;
+
+ protected ResourceType(String name) {
+ this.name = name;
+ this.checkers = null;
+ }
+
+ protected ResourceType(String name, ParameterChecker... checkers) {
+ this.name = name;
+ this.checkers = checkers;
+ }
+
+ /**
+ * Creates a {@link Constraint} with this {@code ResourceType} and
+ * the given {@code values}.
+ *
+ * @param values constraint values
+ * @return newly-created Constraint
+ * @throws IllegalArgumentException when parameter check fails
+ */
+ public final Constraint newConstraint(long... values) {
+ if (!validate(values)) {
+ throw new IllegalArgumentException(this +
+ " values: " + Arrays.toString(values));
+ }
+ return new Constraint(this, values);
+ }
+
+ /**
+ * Checks the validity of parameters. Since a long [] is used to
+ * express the configuration, a length and range check is required.
+ *
+ * Each ResourceType instance can implement its own {@code validate()}
+ * method through Override, for example:
+ *
+ * Each {@code ResourceContainer} implementation must inherit from this class.
+ *
+ * @see ResourceContainer#run(Runnable)
+ */
+
+public abstract class AbstractResourceContainer implements ResourceContainer {
+
+ protected final static AbstractResourceContainer ROOT = new RootContainer();
+
+ public static AbstractResourceContainer root() {
+ return ROOT;
+ }
+
+ public static AbstractResourceContainer current() {
+ if (!sun.misc.VM.isBooted()) {
+ return root();
+ }
+ return SharedSecrets.getJavaLangAccess().getResourceContainer(Thread.currentThread());
+ }
+
+ @Override
+ public void run(Runnable command) {
+ if (getState() != State.RUNNING) {
+ throw new IllegalStateException("container not running");
+ }
+ ResourceContainer container = current();
+ if (container == this) {
+ command.run();
+ return;
+ }
+ if (container != root()) {
+ throw new IllegalStateException("must be in root container " +
+ "before running into non-root container.");
+ }
+ attach();
+ try {
+ command.run();
+ } finally {
+ detach();
+ }
+ }
+
+ /**
+ * Attach to this resource container.
+ * Ensure {@link ResourceContainer#current()} as a root container
+ * before calling this method.
+ *
+ * The implementation class must call {@code super.attach()} to coordinate
+ * with {@link ResourceContainer#current()}
+ */
+ protected void attach() {
+ SharedSecrets.getJavaLangAccess().setResourceContainer(Thread.currentThread(), this);
+ }
+
+ /**
+ * Detach from this resource container and return to root container.
+ *
+ * The implementation class must call {@code super.detach()} to coordinate
+ * with {@link ResourceContainer#current()}
+ */
+ protected void detach() {
+ SharedSecrets.getJavaLangAccess().setResourceContainer(Thread.currentThread(), root());
+ }
+
+ private static class RootContainer extends AbstractResourceContainer {
+ @Override
+ public void run(Runnable command) {
+ AbstractResourceContainer container = current();
+ if (container == root()) {
+ command.run();
+ return;
+ }
+ container.detach();
+ try {
+ command.run();
+ } finally {
+ container.attach();
+ }
+ }
+
+ @Override
+ public ResourceContainer.State getState() {
+ return ResourceContainer.State.RUNNING;
+ }
+
+ @Override
+ protected void attach() {
+ throw new UnsupportedOperationException("should not reach here");
+ }
+
+ @Override
+ protected void detach() {
+ throw new UnsupportedOperationException("should not reach here");
+ }
+
+ @Override
+ public void updateConstraint(Constraint constraint) {
+ throw new UnsupportedOperationException("updateConstraint() is not supported by root container");
+ }
+
+ @Override
+ public Iterable
+ * // Assume we already attach to a non-root resourceContainer1
+ * resourceContainer2.run(command);
+ * // throws exception, because it is illegal to switch between non-root
+ * // ResourceContainers
+ * ResourceContainer.root(() -> resourceContainer2.run(command));
+ *
+ *
+ * @return root container
+ */
+ static ResourceContainer root() {
+ return AbstractResourceContainer.root();
+ }
+
+ /**
+ * Returns the ResourceContainer associated with the current thread.
+ * For threads that do not attach to any user-created ResourceContainer,
+ * {@link #root()} is returned.
+ *
+ * @return current ResourceContainer
+ */
+ static ResourceContainer current() {
+ return AbstractResourceContainer.current();
+ }
+
+ /**
+ * Returns the current ResourceContainer state.
+ *
+ * @return current state.
+ */
+ ResourceContainer.State getState();
+
+ /**
+ * Attach the current thread to the ResourceContainer to run the {@code command},
+ * and detach the ResourceContainer when {@code command} is either normally finished
+ * or terminated by Exception.
+ *
+ * ResourceContainer resourceContainer = ....
+ * assert ResourceContainer.current() == ResourceContainer.root();
+ * resourceContainer.run(() -> {
+ * assert ResourceContainer.current() == resourceContainer;
+ * });
+ * assert ResourceContainer.current() == ResourceContainer.root();
+ *
+ *
+ * @param command the target code
+ */
+ void run(Runnable command);
+
+ /**
+ * Updates {@link Constraint} of this resource container.
+ *
+ * ResourceContainerFactory FACTORY_INSTANCE = new ResourceContainerFactory() {
+ * protected ResourceContainer createContainer(Iterable
+ * Then API users can create ResourceContainer by {@code FACTORY_INSTANCE.createContainer(...)}
+ */
+public interface ResourceContainerFactory {
+
+ /**
+ * Returns the system-wide default factory for this invocation of
+ * the Java virtual machine.
+ *
+ * @return The system-wide default factory
+ */
+ static ResourceContainerFactory defaultFactory() {
+ throw new UnsupportedOperationException("NYI");
+ }
+
+ /**
+ * Builds ResourceContainer with constraints.
+ *
+ * @param constraints the target {@code Constraint}s
+ * @return a newly-created ResourceContainer
+ */
+ ResourceContainer createContainer(Iterable
+ * public class MyResourceType extends ResourceType {
+ * public final static ResourceType CPU_CFS = new MyResourceType();
+ *
+ * public MyResourceType() {}
+ * }
+ *
+ *
+ * public final static ResourceType MY_RESOURCE =
+ * new ResourceType() {
+ * protected boolean validate(long[] values) {
+ * // the check logic
+ * }
+ * };
+ *
+ *
+ * @param values parameter value
+ * @return if parameter is validated
+ */
+ protected boolean validate(long[] values) {
+ if (checkers == null) {
+ return true;
+ }
+ if (checkers.length != values.length) {
+ return false;
+ }
+ return IntStream.range(0, values.length)
+ .allMatch(i -> checkers[i].check(values[i]));
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ protected static ParameterChecker newChecker(long from, long to) {
+ return new ParameterChecker(from, to);
+ }
+
+ /**
+ * Helper class for parameter range check.
+ */
+ protected static class ParameterChecker {
+ private final long from;
+ private final long to;
+
+ protected ParameterChecker(long from, long to) {
+ this.from = from;
+ this.to = to;
+ }
+
+ protected boolean check(long value) {
+ return value >= from && value < to;
+ }
+ }
+}
diff --git a/src/share/classes/com/alibaba/rcm/internal/AbstractResourceContainer.java b/src/share/classes/com/alibaba/rcm/internal/AbstractResourceContainer.java
new file mode 100644
index 0000000000..abb0039e05
--- /dev/null
+++ b/src/share/classes/com/alibaba/rcm/internal/AbstractResourceContainer.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Alibaba designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+package com.alibaba.rcm.internal;
+
+import com.alibaba.rcm.Constraint;
+import com.alibaba.rcm.ResourceContainer;
+import sun.misc.SharedSecrets;
+
+import java.util.Collections;
+
+/**
+ * A skeletal implementation of {@link ResourceContainer} that practices
+ * the attach/detach paradigm described in {@link ResourceContainer#run(Runnable)}.
+ * System
class contains several useful class fields
* and methods. It cannot be instantiated.
@@ -1269,6 +1271,16 @@ public Thread newThreadWithAcc(Runnable target, AccessControlContext acc) {
public void invokeFinalize(Object o) throws Throwable {
o.finalize();
}
+
+ @Override
+ public void setResourceContainer(Thread thread, AbstractResourceContainer container) {
+ thread.resourceContainer = container;
+ }
+
+ @Override
+ public AbstractResourceContainer getResourceContainer(Thread thread) {
+ return thread.resourceContainer;
+ }
});
}
}
diff --git a/src/share/classes/java/lang/Thread.java b/src/share/classes/java/lang/Thread.java
index 0cd6778684..01bc46f154 100644
--- a/src/share/classes/java/lang/Thread.java
+++ b/src/share/classes/java/lang/Thread.java
@@ -36,6 +36,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.LockSupport;
+import com.alibaba.rcm.internal.AbstractResourceContainer;
import sun.nio.ch.Interruptible;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
@@ -213,6 +214,10 @@ private static synchronized int nextThreadNum() {
private volatile int threadStatus = 0;
+ /**
+ * The thread attached {@code ResourceContainer}
+ */
+ AbstractResourceContainer resourceContainer;
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
@@ -423,6 +428,10 @@ private void init(ThreadGroup g, Runnable target, String name,
/* Set thread ID */
tid = nextThreadID();
+
+ /* com.alibaba.rcm API */
+ this.resourceContainer = parent.resourceContainer != null ?
+ parent.resourceContainer : AbstractResourceContainer.root();
}
/**
diff --git a/src/share/classes/sun/misc/JavaLangAccess.java b/src/share/classes/sun/misc/JavaLangAccess.java
index 812f88e209..ec55a29e6f 100644
--- a/src/share/classes/sun/misc/JavaLangAccess.java
+++ b/src/share/classes/sun/misc/JavaLangAccess.java
@@ -30,6 +30,7 @@
import java.security.AccessControlContext;
import java.util.Map;
+import com.alibaba.rcm.internal.AbstractResourceContainer;
import sun.reflect.ConstantPool;
import sun.reflect.annotation.AnnotationType;
import sun.nio.ch.Interruptible;
@@ -132,4 +133,16 @@ public interface JavaLangAccess {
* Invokes the finalize method of the given object.
*/
void invokeFinalize(Object o) throws Throwable;
+
+ /**
+ * Set the value of {@code thread.resourceContainer}
+ *
+ * @param thread target thread to be modified
+ */
+ void setResourceContainer(Thread thread, AbstractResourceContainer container);
+
+ /**
+ * Get the reference to the thread attached {@code ResourceContainer}
+ */
+ AbstractResourceContainer getResourceContainer(Thread thread);
}
diff --git a/test/com/alibaba/rcm/TestConfiguration.java b/test/com/alibaba/rcm/TestConfiguration.java
new file mode 100644
index 0000000000..b6d4609733
--- /dev/null
+++ b/test/com/alibaba/rcm/TestConfiguration.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Alibaba designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test builder and update constraint
+ * @library /lib/testlibrary
+ * @run main TestConfiguration
+ */
+
+import demo.MyResourceContainer;
+import demo.MyResourceFactory;
+import demo.MyResourceType;
+
+import java.util.Arrays;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import static jdk.testlibrary.Asserts.assertTrue;
+
+public class TestConfiguration {
+ public static void main(String[] args) {
+
+ MyResourceContainer mc = (MyResourceContainer) MyResourceFactory.INSTANCE.createContainer(Arrays.asList(
+ MyResourceType.MY_RESOURCE1.newConstraint(),
+ MyResourceType.MY_RESOURCE2.newConstraint()));
+
+
+ assertTrue(iterator2Stream(mc.operations.iterator()).collect(Collectors.toSet())
+ .equals(new HashSet<>(Arrays.asList("update MY_RESOURCE1", "update MY_RESOURCE2"))));
+
+ mc.updateConstraint(MyResourceType.MY_RESOURCE2.newConstraint());
+
+ assertTrue(iterator2Stream(mc.operations.iterator()).collect(Collectors.toSet())
+ .equals(new HashSet<>(Arrays.asList("update MY_RESOURCE1", "update MY_RESOURCE2", "update MY_RESOURCE2"))));
+ }
+
+ private static