diff options
| -rw-r--r-- | CONTRIBUTING.md | 11 | ||||
| -rw-r--r-- | api/src/main/java/io/opencensus/common/Duration.java | 5 | ||||
| -rw-r--r-- | api/src/main/java/io/opencensus/common/TimeUtils.java | 31 | ||||
| -rw-r--r-- | api/src/main/java/io/opencensus/common/Timestamp.java | 11 | ||||
| -rw-r--r-- | api/src/main/java/io/opencensus/internal/Utils.java | 30 | ||||
| -rw-r--r-- | api/src/test/java/io/opencensus/common/TimeUtilsTest.java | 60 | ||||
| -rw-r--r-- | api/src/test/java/io/opencensus/internal/UtilsTest.java | 29 | ||||
| -rw-r--r-- | buildscripts/checkstyle.xml | 4 | ||||
| -rw-r--r-- | buildscripts/import-control.xml | 189 | ||||
| -rw-r--r-- | impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java | 11 |
10 files changed, 306 insertions, 75 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 58137796..c76ccd3e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -108,6 +108,17 @@ applied to types. However, it uses `javax.annotation.Nullable` in API method signatures whenever possible, so that the annotations can be uncommented and be included in .class files and Javadocs. +### Checkstyle import control + +This project uses Checkstyle to specify the allowed dependencies between +packages, using its ImportControl feature +(http://checkstyle.sourceforge.net/config_imports.html#ImportControl). +`buildscripts/import-control.xml` specifies the allowed imports and contains +some guidelines on OpenCensus' inter-package dependencies. An error messsage +such as +`Disallowed import - edu.umd.cs.findbugs.annotations.SuppressFBWarnings. [ImportControl]` +could mean that `import-control.xml` needs to be updated. + ## Proposing changes Create a Pull Request with your changes. The continuous integration build will diff --git a/api/src/main/java/io/opencensus/common/Duration.java b/api/src/main/java/io/opencensus/common/Duration.java index 2d90f506..4b7bb367 100644 --- a/api/src/main/java/io/opencensus/common/Duration.java +++ b/api/src/main/java/io/opencensus/common/Duration.java @@ -22,7 +22,6 @@ import static io.opencensus.common.TimeUtils.MILLIS_PER_SECOND; import static io.opencensus.common.TimeUtils.NANOS_PER_MILLI; import com.google.auto.value.AutoValue; -import io.opencensus.internal.Utils; import java.util.concurrent.TimeUnit; import javax.annotation.concurrent.Immutable; @@ -115,11 +114,11 @@ public abstract class Duration implements Comparable<Duration> { */ @Override public int compareTo(Duration otherDuration) { - int cmp = Utils.compareLongs(getSeconds(), otherDuration.getSeconds()); + int cmp = TimeUtils.compareLongs(getSeconds(), otherDuration.getSeconds()); if (cmp != 0) { return cmp; } - return Utils.compareLongs(getNanos(), otherDuration.getNanos()); + return TimeUtils.compareLongs(getNanos(), otherDuration.getNanos()); } Duration() {} diff --git a/api/src/main/java/io/opencensus/common/TimeUtils.java b/api/src/main/java/io/opencensus/common/TimeUtils.java index 2429cc90..db119e2e 100644 --- a/api/src/main/java/io/opencensus/common/TimeUtils.java +++ b/api/src/main/java/io/opencensus/common/TimeUtils.java @@ -16,6 +16,8 @@ package io.opencensus.common; +import java.math.BigInteger; + /** Util class for {@link Timestamp} and {@link Duration}. */ final class TimeUtils { static final long MAX_SECONDS = 315576000000L; @@ -25,4 +27,33 @@ final class TimeUtils { static final long NANOS_PER_SECOND = NANOS_PER_MILLI * MILLIS_PER_SECOND; private TimeUtils() {} + + /** + * Compares two longs. This functionality is provided by {@code Long.compare(long, long)} in Java + * 7. + */ + static int compareLongs(long x, long y) { + if (x < y) { + return -1; + } else if (x == y) { + return 0; + } else { + return 1; + } + } + + private static final BigInteger MAX_LONG_VALUE = BigInteger.valueOf(Long.MAX_VALUE); + private static final BigInteger MIN_LONG_VALUE = BigInteger.valueOf(Long.MIN_VALUE); + + /** + * Adds two longs and throws an {@link ArithmeticException} if the result overflows. This + * functionality is provided by {@code Math.addExact(long, long)} in Java 8. + */ + static long checkedAdd(long x, long y) { + BigInteger sum = BigInteger.valueOf(x).add(BigInteger.valueOf(y)); + if (sum.compareTo(MAX_LONG_VALUE) > 0 || sum.compareTo(MIN_LONG_VALUE) < 0) { + throw new ArithmeticException("Long sum overflow: x=" + x + ", y=" + y); + } + return x + y; + } } diff --git a/api/src/main/java/io/opencensus/common/Timestamp.java b/api/src/main/java/io/opencensus/common/Timestamp.java index 86c14cda..e3aae3a6 100644 --- a/api/src/main/java/io/opencensus/common/Timestamp.java +++ b/api/src/main/java/io/opencensus/common/Timestamp.java @@ -23,7 +23,6 @@ import static io.opencensus.common.TimeUtils.NANOS_PER_MILLI; import static io.opencensus.common.TimeUtils.NANOS_PER_SECOND; import com.google.auto.value.AutoValue; -import io.opencensus.internal.Utils; import java.math.BigDecimal; import java.math.RoundingMode; import javax.annotation.concurrent.Immutable; @@ -153,11 +152,11 @@ public abstract class Timestamp implements Comparable<Timestamp> { */ @Override public int compareTo(Timestamp otherTimestamp) { - int cmp = Utils.compareLongs(getSeconds(), otherTimestamp.getSeconds()); + int cmp = TimeUtils.compareLongs(getSeconds(), otherTimestamp.getSeconds()); if (cmp != 0) { return cmp; } - return Utils.compareLongs(getNanos(), otherTimestamp.getNanos()); + return TimeUtils.compareLongs(getNanos(), otherTimestamp.getNanos()); } // Returns a Timestamp with the specified duration added. @@ -165,8 +164,8 @@ public abstract class Timestamp implements Comparable<Timestamp> { if ((secondsToAdd | nanosToAdd) == 0) { return this; } - long epochSec = Utils.checkedAdd(getSeconds(), secondsToAdd); - epochSec = Utils.checkedAdd(epochSec, nanosToAdd / NANOS_PER_SECOND); + long epochSec = TimeUtils.checkedAdd(getSeconds(), secondsToAdd); + epochSec = TimeUtils.checkedAdd(epochSec, nanosToAdd / NANOS_PER_SECOND); nanosToAdd = nanosToAdd % NANOS_PER_SECOND; long nanoAdjustment = getNanos() + nanosToAdd; // safe int + NANOS_PER_SECOND return ofEpochSecond(epochSec, nanoAdjustment); @@ -175,7 +174,7 @@ public abstract class Timestamp implements Comparable<Timestamp> { // Returns a Timestamp calculated using seconds from the epoch and nanosecond fraction of // second (arbitrary number of nanoseconds). private static Timestamp ofEpochSecond(long epochSecond, long nanoAdjustment) { - long secs = Utils.checkedAdd(epochSecond, floorDiv(nanoAdjustment, NANOS_PER_SECOND)); + long secs = TimeUtils.checkedAdd(epochSecond, floorDiv(nanoAdjustment, NANOS_PER_SECOND)); int nos = (int) floorMod(nanoAdjustment, NANOS_PER_SECOND); return create(secs, nos); } diff --git a/api/src/main/java/io/opencensus/internal/Utils.java b/api/src/main/java/io/opencensus/internal/Utils.java index 0b27511d..1ac2324e 100644 --- a/api/src/main/java/io/opencensus/internal/Utils.java +++ b/api/src/main/java/io/opencensus/internal/Utils.java @@ -16,7 +16,6 @@ package io.opencensus.internal; -import java.math.BigInteger; import javax.annotation.Nullable; /*>>> @@ -94,33 +93,4 @@ public final class Utils { public static boolean equalsObjects(@Nullable Object x, @Nullable Object y) { return x == null ? y == null : x.equals(y); } - - /** - * Compares two longs. This functionality is provided by {@code Long.compare(long, long)} in Java - * 7. - */ - public static int compareLongs(long x, long y) { - if (x < y) { - return -1; - } else if (x == y) { - return 0; - } else { - return 1; - } - } - - private static final BigInteger MAX_LONG_VALUE = BigInteger.valueOf(Long.MAX_VALUE); - private static final BigInteger MIN_LONG_VALUE = BigInteger.valueOf(Long.MIN_VALUE); - - /** - * Adds two longs and throws an {@link ArithmeticException} if the result overflows. This - * functionality is provided by {@code Math.addExact(long, long)} in Java 8. - */ - public static long checkedAdd(long x, long y) { - BigInteger sum = BigInteger.valueOf(x).add(BigInteger.valueOf(y)); - if (sum.compareTo(MAX_LONG_VALUE) > 0 || sum.compareTo(MIN_LONG_VALUE) < 0) { - throw new ArithmeticException("Long sum overflow: x=" + x + ", y=" + y); - } - return x + y; - } } diff --git a/api/src/test/java/io/opencensus/common/TimeUtilsTest.java b/api/src/test/java/io/opencensus/common/TimeUtilsTest.java new file mode 100644 index 00000000..d6228566 --- /dev/null +++ b/api/src/test/java/io/opencensus/common/TimeUtilsTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * 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. + */ + +package io.opencensus.common; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link TimeUtils}. */ +@RunWith(JUnit4.class) +public final class TimeUtilsTest { + + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test + public void compareLongs() { + assertThat(TimeUtils.compareLongs(-1L, 1L)).isLessThan(0); + assertThat(TimeUtils.compareLongs(10L, 10L)).isEqualTo(0); + assertThat(TimeUtils.compareLongs(1L, 0L)).isGreaterThan(0); + } + + @Test + public void checkedAdd_TooLow() { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Long sum overflow: x=-9223372036854775807, y=-2"); + TimeUtils.checkedAdd(Long.MIN_VALUE + 1, -2); + } + + @Test + public void checkedAdd_TooHigh() { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Long sum overflow: x=9223372036854775806, y=2"); + TimeUtils.checkedAdd(Long.MAX_VALUE - 1, 2); + } + + @Test + public void checkedAdd_Valid() { + assertThat(TimeUtils.checkedAdd(1, 2)).isEqualTo(3); + assertThat(TimeUtils.checkedAdd(Integer.MAX_VALUE, Integer.MAX_VALUE)) + .isEqualTo(2L * Integer.MAX_VALUE); + } +} diff --git a/api/src/test/java/io/opencensus/internal/UtilsTest.java b/api/src/test/java/io/opencensus/internal/UtilsTest.java index dea7c843..983264f6 100644 --- a/api/src/test/java/io/opencensus/internal/UtilsTest.java +++ b/api/src/test/java/io/opencensus/internal/UtilsTest.java @@ -16,7 +16,6 @@ package io.opencensus.internal; -import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -103,32 +102,4 @@ public final class UtilsTest { assertFalse(Utils.equalsObjects(new Object(), null)); assertFalse(Utils.equalsObjects(new Object(), new Object())); } - - @Test - public void compareLongs() { - assertThat(Utils.compareLongs(-1L, 1L)).isLessThan(0); - assertThat(Utils.compareLongs(10L, 10L)).isEqualTo(0); - assertThat(Utils.compareLongs(1L, 0L)).isGreaterThan(0); - } - - @Test - public void checkedAdd_TooLow() { - thrown.expect(ArithmeticException.class); - thrown.expectMessage("Long sum overflow: x=-9223372036854775807, y=-2"); - Utils.checkedAdd(Long.MIN_VALUE + 1, -2); - } - - @Test - public void checkedAdd_TooHigh() { - thrown.expect(ArithmeticException.class); - thrown.expectMessage("Long sum overflow: x=9223372036854775806, y=2"); - Utils.checkedAdd(Long.MAX_VALUE - 1, 2); - } - - @Test - public void checkedAdd_Valid() { - assertThat(Utils.checkedAdd(1, 2)).isEqualTo(3); - assertThat(Utils.checkedAdd(Integer.MAX_VALUE, Integer.MAX_VALUE)) - .isEqualTo(2L * Integer.MAX_VALUE); - } } diff --git a/buildscripts/checkstyle.xml b/buildscripts/checkstyle.xml index fdea0c5e..6c7cfa49 100644 --- a/buildscripts/checkstyle.xml +++ b/buildscripts/checkstyle.xml @@ -227,6 +227,10 @@ <module name="CommentsIndentation"/> <module name="FileContentsHolder"/> <module name="SuppressWarningsHolder"/> + <module name="ImportControl"> + <property name="file" value="${rootDir}/buildscripts/import-control.xml"/> + <property name="path" value="^.*[\\/]src[\\/]main[\\/]java[\\/].*$"/> + </module> </module> <module name="SuppressionCommentFilter"/> <module name="SuppressWarningsFilter"/> diff --git a/buildscripts/import-control.xml b/buildscripts/import-control.xml new file mode 100644 index 00000000..75ae826a --- /dev/null +++ b/buildscripts/import-control.xml @@ -0,0 +1,189 @@ +<?xml version="1.0"?> +<!DOCTYPE import-control PUBLIC + "-//Puppy Crawl//DTD Import Control 1.3//EN" + "http://checkstyle.sourceforge.net/dtds/import_control_1_3.dtd"> + +<!-- + +General guidelines on imports: + +- 'stats' depends on 'tags', but 'tags' shouldn't depend on 'stats' or 'trace'. + 'stats'/'tags' and 'trace' should remain independent, where possible. + +- Packages should not be split between artifacts. + +- 'internal' packages should only be imported by packages within the same + artifact. + +- Since we are trying to remove dependencies on Guava (issue #1113), we should + avoid adding any new Guava imports here, especially in the API. + +--> + +<import-control pkg="io.opencensus"> + <allow pkg="com.google.auto.value"/> + <allow pkg="com.google.errorprone.annotations"/> + <allow pkg="java"/> + <allow pkg="javax"/> + <allow class="io.grpc.Context"/> + <subpackage name="common"> + <allow pkg="io.opencensus.common"/> + </subpackage> + <subpackage name="internal"> + <allow pkg="io.opencensus.common"/> + <allow pkg="io.opencensus.internal"/> + </subpackage> + <subpackage name="tags"> + <allow pkg="io.opencensus.common"/> + <allow pkg="io.opencensus.internal"/> + <allow pkg="io.opencensus.tags"/> + </subpackage> + <subpackage name="stats"> + <allow pkg="io.opencensus.common"/> + <allow pkg="io.opencensus.internal"/> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + </subpackage> + <subpackage name="trace"> + <allow pkg="io.opencensus.common"/> + <allow pkg="io.opencensus.internal"/> + <allow pkg="io.opencensus.trace"/> + + <!-- TODO(#1081): Remove this dependency on Guava. --> + <allow class="com.google.common.io.BaseEncoding"/> + + <!-- These dependencies on impl/implcore are only needed by --> + <!-- io.opencensus.trace.TraceComponentImpl and io.opencensus.trace.TraceComponentImplLite, --> + <!-- which are deprecated. --> + <allow class="io.opencensus.impl.internal.DisruptorEventQueue"/> + <allow class="io.opencensus.impl.trace.internal.ThreadLocalRandomHandler"/> + <allow class="io.opencensus.implcore.common.MillisClock"/> + <allow class="io.opencensus.implcore.internal.SimpleEventQueue"/> + <allow class="io.opencensus.implcore.trace.TraceComponentImplBase"/> + <allow class="io.opencensus.implcore.trace.internal.RandomHandler.SecureRandomHandler"/> + </subpackage> + <subpackage name="contrib"> + <allow pkg="com.google.common"/> + <allow pkg="io.opencensus.common"/> + <subpackage name="agent"> + <allow pkg="com.google.auto"/> + <allow pkg="com.typesafe.config"/> + <allow pkg="edu.umd.cs.findbugs.annotations"/> + <allow pkg="io.opencensus.contrib.agent"/> + <allow pkg="io.opencensus.trace"/> + <allow pkg="net.bytebuddy"/> + </subpackage> + <subpackage name="grpc.metrics"> + <allow pkg="io.opencensus.contrib.grpc.metrics"/> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + </subpackage> + <subpackage name="http.util"> + <allow pkg="io.opencensus.contrib.http.util"/> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + <allow pkg="io.opencensus.trace"/> + </subpackage> + <subpackage name="zpages"> + <allow pkg="com.sun.net.httpserver"/> + <allow pkg="io.opencensus.contrib.grpc.metrics"/> + <allow pkg="io.opencensus.contrib.zpages"/> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + <allow pkg="io.opencensus.trace"/> + </subpackage> + </subpackage> + <subpackage name="exporter"> + <allow pkg="com.google.common"/> + <allow pkg="io.opencensus.common"/> + <subpackage name="stats"> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + <subpackage name="prometheus"> + <allow pkg="io.opencensus.exporter.stats.prometheus"/> + <allow pkg="io.opencensus.trace"/> + <allow pkg="io.prometheus.client"/> + </subpackage> + <subpackage name="signalfx"> + <allow pkg="com.signalfx"/> + <allow pkg="io.opencensus.exporter.stats.signalfx"/> + <allow pkg="io.opencensus.trace"/> + </subpackage> + <subpackage name="stackdriver"> + <allow pkg="com.google"/> + <allow pkg="io.opencensus.exporter.stats.stackdriver"/> + <allow pkg="io.opencensus.trace"/> + </subpackage> + </subpackage> + <subpackage name="trace"> + <allow pkg="io.opencensus.trace"/> + <subpackage name="instana"> + <allow pkg="io.opencensus.exporter.trace.instana"/> + </subpackage> + <subpackage name="jaeger"> + <allow pkg="com.uber.jaeger"/> + <allow pkg="io.opencensus.exporter.trace.jaeger"/> + <allow pkg="org.apache.thrift"/> + </subpackage> + <subpackage name="stackdriver"> + <allow pkg="com.google"/> + <allow pkg="io.opencensus.exporter.trace.stackdriver"/> + </subpackage> + <subpackage name="zipkin"> + <allow pkg="io.opencensus.exporter.trace.zipkin"/> + <allow pkg="zipkin2"/> + </subpackage> + </subpackage> + </subpackage> + <subpackage name="implcore"> + <allow pkg="com.google.common"/> + <allow pkg="io.opencensus.common"/> + <allow pkg="io.opencensus.implcore"/> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + <allow pkg="io.opencensus.trace"/> + </subpackage> + <subpackage name="impl"> + <allow pkg="com.lmax.disruptor"/> + <allow pkg="io.opencensus.common"/> + <allow pkg="io.opencensus.impl"/> + <allow pkg="io.opencensus.implcore"/> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + <allow pkg="io.opencensus.trace"/> + </subpackage> + <subpackage name="impllite"> + <allow pkg="io.opencensus.common"/> + <allow pkg="io.opencensus.implcore"/> + <allow pkg="io.opencensus.impllite"/> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + <allow pkg="io.opencensus.trace"/> + </subpackage> + <subpackage name="testing"> + <allow pkg="com.google.common"/> + <allow pkg="io.opencensus.common"/> + <subpackage name="common"> + <allow pkg="io.opencensus.testing.common"/> + </subpackage> + <subpackage name="export"> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + <allow pkg="io.opencensus.testing.export"/> + <allow pkg="io.opencensus.trace"/> + </subpackage> + </subpackage> + <subpackage name="examples"> + <allow pkg="com.google.common"/> + <allow pkg="io.grpc"/> + <allow pkg="io.opencensus.common"/> + <allow pkg="io.opencensus.contrib"/> + <allow pkg="io.opencensus.examples"/> + <allow pkg="io.opencensus.exporter"/> + <allow pkg="io.opencensus.stats"/> + <allow pkg="io.opencensus.tags"/> + <allow pkg="io.opencensus.testing.export"/> + <allow pkg="io.opencensus.trace"/> + <allow pkg="io.prometheus"/> + </subpackage> +</import-control> diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java index bb08222e..a2438008 100644 --- a/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java +++ b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java @@ -18,7 +18,7 @@ package io.opencensus.implcore.trace.propagation; import static com.google.common.base.Preconditions.checkNotNull; -import io.opencensus.internal.DefaultVisibilityForTesting; +import com.google.common.annotations.VisibleForTesting; import io.opencensus.trace.SpanContext; import io.opencensus.trace.SpanId; import io.opencensus.trace.TraceId; @@ -71,20 +71,17 @@ final class BinaryFormatImpl extends BinaryFormat { // parsing when you hit an unknown field, it does not suggest that fields must be declared in // ID order. Rather it only groups by data type order, in this case Trace Context // https://github.com/census-instrumentation/opencensus-specs/blob/master/encodings/BinaryEncoding.md#deserialization-rules - @DefaultVisibilityForTesting - static final int TRACE_ID_FIELD_ID_OFFSET = VERSION_ID_OFFSET + ID_SIZE; + @VisibleForTesting static final int TRACE_ID_FIELD_ID_OFFSET = VERSION_ID_OFFSET + ID_SIZE; private static final int TRACE_ID_OFFSET = TRACE_ID_FIELD_ID_OFFSET + ID_SIZE; private static final byte SPAN_ID_FIELD_ID = 1; - @DefaultVisibilityForTesting - static final int SPAN_ID_FIELD_ID_OFFSET = TRACE_ID_OFFSET + TraceId.SIZE; + @VisibleForTesting static final int SPAN_ID_FIELD_ID_OFFSET = TRACE_ID_OFFSET + TraceId.SIZE; private static final int SPAN_ID_OFFSET = SPAN_ID_FIELD_ID_OFFSET + ID_SIZE; private static final byte TRACE_OPTION_FIELD_ID = 2; - @DefaultVisibilityForTesting - static final int TRACE_OPTION_FIELD_ID_OFFSET = SPAN_ID_OFFSET + SpanId.SIZE; + @VisibleForTesting static final int TRACE_OPTION_FIELD_ID_OFFSET = SPAN_ID_OFFSET + SpanId.SIZE; private static final int TRACE_OPTIONS_OFFSET = TRACE_OPTION_FIELD_ID_OFFSET + ID_SIZE; /** Version, Trace and Span IDs are required fields. */ |
