Repository: jhipster/generator-jhipster (ts)

Issue:
Identical stacktraces for builds involving infinispan
Two different builds:
  - [From #11606](https://github.com/jhipster/generator-jhipster/pull/11606/checks?check_run_id=602335602)
  - [From #11588](https://github.com/jhipster/generator-jhipster/pull/11588/checks?check_run_id=607930809)

We see this stacktrace every time, but the builds still pass (or fail for unrelated reasons, but let's not care about the failing ones):
```
2020-04-22T09:56:32.6320852Z java.lang.IllegalArgumentException: No marshaller registered for Java type org.hibernate.cache.internal.CacheKeyImplementation
2020-04-22T09:56:32.6321233Z 	at org.infinispan.protostream.impl.SerializationContextImpl.getMarshallerDelegate(SerializationContextImpl.java:279)
2020-04-22T09:56:32.6321990Z 	at org.infinispan.protostream.WrappedMessage.writeMessage(WrappedMessage.java:240)
2020-04-22T09:56:32.6322310Z 	at org.infinispan.protostream.ProtobufUtil.toWrappedStream(ProtobufUtil.java:196)
2020-04-22T09:56:32.6322693Z 	at org.infinispan.marshall.persistence.impl.PersistenceMarshallerImpl.objectToBuffer(PersistenceMarshallerImpl.java:157)
2020-04-22T09:56:32.6323449Z 	at org.infinispan.marshall.persistence.impl.PersistenceMarshallerImpl.objectToByteBuffer(PersistenceMarshallerImpl.java:137)
2020-04-22T09:56:32.6323741Z 	at org.infinispan.marshall.persistence.impl.PersistenceMarshallerImpl.objectToByteBuffer(PersistenceMarshallerImpl.java:145)
2020-04-22T09:56:32.6323976Z 	at org.infinispan.marshall.core.GlobalMarshaller.writeRawUnknown(GlobalMarshaller.java:638)
2020-04-22T09:56:32.6324332Z 	at org.infinispan.marshall.core.GlobalMarshaller.writeUnknown(GlobalMarshaller.java:627)
2020-04-22T09:56:32.6324568Z 	at org.infinispan.marshall.core.GlobalMarshaller.writeUnknown(GlobalMarshaller.java:618)
2020-04-22T09:56:32.6324785Z 	at org.infinispan.marshall.core.GlobalMarshaller.writeNonNullableObject(GlobalMarshaller.java:384)
2020-04-22T09:56:32.6325008Z 	at org.infinispan.marshall.core.GlobalMarshaller.writeNullableObject(GlobalMarshaller.java:352)
2020-04-22T09:56:32.6325220Z 	at org.infinispan.marshall.core.BytesObjectOutput.writeObject(BytesObjectOutput.java:26)
2020-04-22T09:56:32.6325424Z 	at org.infinispan.commons.marshall.MarshallUtil.marshallArray(MarshallUtil.java:191)
2020-04-22T09:56:32.6325659Z 	at org.infinispan.hibernate.cache.commons.util.EndInvalidationCommand.writeTo(EndInvalidationCommand.java:67)
2020-04-22T09:56:32.6325898Z 	at org.infinispan.marshall.exts.ReplicableCommandExternalizer.writeCommandParameters(ReplicableCommandExternalizer.java:70)
...
```

Possible fix described [here](https://stackoverflow.com/questions/58815818/infinispan-10-0-1-final-no-marshaller-registered-for-java-type-java-util-uuid).

Pull request:
add JBossUserMarshaller to infinispan configuration
Fix #11629 

-   [ ] [All continuous integration tests](https://github.com/jhipster/generator-jhipster/actions) are green
-   [x] Tests are added where necessary
-   [x] Documentation is added/updated where necessary
-   [x] Coding Rules & Commit Guidelines as per our [CONTRIBUTING.md document](https://github.com/jhipster/generator-jhipster/blob/master/CONTRIBUTING.md) are followed

<!--
Please also reference the issue number in a commit message to [automatically close the related Github issue](https://help.github.com/articles/closing-issues-via-commit-messages/)

Note: It is also possible to add `[skip ci]` or `[ci skip]` to your commit message to skip continuous integration tests
-->


Patch:
diff --git a/README.md b/README.md
index 1b78c976e5..f86f7a3322 100644
--- a/README.md
+++ b/README.md
@@ -135,6 +135,11 @@ Support this project by becoming a sponsor! [Become a sponsor](https://opencolle
           <img width="50em" src="https://www.jhipster.tech/images/open-collective/unscramblex.png">
         </a>
       </td>
+      <td align="center" valign="middle">
+        <a href="https://manwoman.co/pl" target="_blank">
+          <img width="50em" src="https://www.jhipster.tech/images/open-collective/manwoman.png">
+        </a>
+      </td>
     </tr>
   </tbody>
 </table>
diff --git a/generators/entity-client/files.js b/generators/entity-client/files.js
index c6d9b57524..59ab759932 100644
--- a/generators/entity-client/files.js
+++ b/generators/entity-client/files.js
@@ -270,7 +270,11 @@ function addEnumerationFiles(generator, templateDir, clientFolder) {
     generator.fields.forEach(field => {
         if (field.fieldIsEnum === true) {
             const enumFileName = _.kebabCase(field.fieldType);
-            const enumInfo = utils.buildEnumInfo(field, generator.angularAppName, generator.packageName, generator.clientRootFolder);
+            const enumInfo = {
+                ...utils.getEnumInfo(field, generator.clientRootFolder),
+                angularAppName: generator.angularAppName,
+                packageName: generator.packageName
+            };
             if (!generator.skipClient) {
                 generator.template(
                     `${generator.fetchFromInstalledJHipster(
diff --git a/generators/entity-client/templates/angular/src/main/webapp/app/entities/entity-management-update.component.html.ejs b/generators/entity-client/templates/angular/src/main/webapp/app/entities/entity-management-update.component.html.ejs
index 2197c7473c..d5ded6f3d3 100644
--- a/generators/entity-client/templates/angular/src/main/webapp/app/entities/entity-management-update.component.html.ejs
+++ b/generators/entity-client/templates/angular/src/main/webapp/app/entities/entity-management-update.component.html.ejs
@@ -55,10 +55,10 @@ _%>
     <%_ if (fields[idx].fieldIsEnum) { _%>
                     <select class="form-control" name="<%= fieldName %>" formControlName="<%= fieldName %>" id="field_<%= fieldName %>">
         <%_ const enumPrefix = angularAppName + '.'+ fieldType;
-            const values = fields[idx].fieldValues.replace(/\s/g, '').split(',');
+            const values = getEnumValuesWithCustomValues(fields[idx].fieldValues);
             for (key in values) {
-                const value = values[key]; _%>
-                        <option value="<%= value %>"><% if (enableTranslation) { %>{{ '<%= enumPrefix %>.<%= value %>' | translate }}<% } else { %><%= value %><% } %></option>
+                const enumValue = values[key]; _%>
+                        <option value="<%= enumValue.name %>"><% if (enableTranslation) { %>{{ '<%= enumPrefix %>.<%= enumValue.name %>' | translate }}<% } else { %><%= enumValue.value %><% } %></option>
         <%_ } _%>
                     </select>
     <%_ } else { _%>
diff --git a/generators/entity-client/templates/angular/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs b/generators/entity-client/templates/angular/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
index 4a76dabb5f..22d7f2068b 100644
--- a/generators/entity-client/templates/angular/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
+++ b/generators/entity-client/templates/angular/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
@@ -17,11 +17,7 @@
  limitations under the License.
 -%>
 export const enum <%= enumName %> {
-<% enumsWithCustomValue.forEach((enumWithCustomValue, index) => { %>
-  <% if (!enumWithCustomValue.value) { %>
-  <%= enumWithCustomValue.name %> = '<%= enumWithCustomValue.name %>'
-  <% } else { %>
-  <%= enumWithCustomValue.name %> = '<%= enumWithCustomValue.value %>'
-  <% } %><% if (index < enumsWithCustomValue.length - 1) { %>,<% } %>
+<% enumValues.forEach((enumValue, index) => { %>
+  <%= enumValue.name %> = '<%= enumValue.value %>'<% if (index < enumValues.length - 1) { %>,<% } %>
 <% }); %>
 }
diff --git a/generators/entity-client/templates/react/src/main/webapp/app/entities/entity-update.tsx.ejs b/generators/entity-client/templates/react/src/main/webapp/app/entities/entity-update.tsx.ejs
index b0f1e5d0c3..3d124dedff 100644
--- a/generators/entity-client/templates/react/src/main/webapp/app/entities/entity-update.tsx.ejs
+++ b/generators/entity-client/templates/react/src/main/webapp/app/entities/entity-update.tsx.ejs
@@ -278,7 +278,7 @@ export const <%= entityReactName %>Update = (props: I<%= entityReactName %>Updat
             />
           <%_
             } else if (fields[idx].fieldIsEnum) {
-              const values = fields[idx].fieldValues.replace(/\s/g, '').split(',');
+              const enumValues = getEnumValuesWithCustomValues(fields[idx].fieldValues);
           _%>
             <Label id="<%= fieldName %>Label" for="<%= entityFileName %>-<%= fieldName %>">
               <Translate contentKey="<%= i18nKeyPrefix %>.<%= fieldName %>">
@@ -290,21 +290,20 @@ export const <%= entityReactName %>Update = (props: I<%= entityReactName %>Updat
               type="select"
               className="form-control"
               name="<%= fieldName %>"
-              value={(!isNew && <%= entityInstance %>Entity.<%= fieldName %>) || '<%= values[0] %>'}
+              value={(!isNew && <%= entityInstance %>Entity.<%= fieldName %>) || '<%= enumValues[0].name %>'}
             >
             <%_
               const enumPrefix = angularAppName + '.'+ fieldType;
-              for (key in values) {
-                  const value = values[key];
+              enumValues.forEach(enumValue => {
             _%>
-              <option value="<%= value %>">
+              <option value="<%= enumValue.name %>">
               <%_ if (enableTranslation) { _%>
-                {translate("<%= enumPrefix %>.<%= value %>")}
+                {translate("<%= enumPrefix %>.<%= enumValue.name %>")}
               <%_ } else { _%>
-                <%= value %>
+                <%= enumValue.value %>
               <%_ } _%>
               </option>
-            <%_ } _%>
+            <%_ }); _%>
             </AvInput>
           <%_ } else if (['Integer', 'Long', 'Float', 'Double'].includes(fieldType)) { _%>
             <Label id="<%= fieldName %>Label" for="<%= entityFileName %>-<%= fieldName %>">
diff --git a/generators/entity-client/templates/react/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs b/generators/entity-client/templates/react/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
index e57574c6e1..22d7f2068b 100644
--- a/generators/entity-client/templates/react/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
+++ b/generators/entity-client/templates/react/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
@@ -17,11 +17,7 @@
  limitations under the License.
 -%>
 export const enum <%= enumName %> {
-<% enumsWithCustomValue.forEach((enumWithCustomValue, index) => { %>
-<% if (!enumWithCustomValue.value) { %>
-  <%= enumWithCustomValue.name %> = '<%= enumWithCustomValue.name %>'
-<% } else { %>
-  <%= enumWithCustomValue.name %> = '<%= enumWithCustomValue.value %>'
-<% } %><% if (index < enumsWithCustomValue.length - 1) { %>,<% } %>
+<% enumValues.forEach((enumValue, index) => { %>
+  <%= enumValue.name %> = '<%= enumValue.value %>'<% if (index < enumValues.length - 1) { %>,<% } %>
 <% }); %>
 }
diff --git a/generators/entity-i18n/files.js b/generators/entity-i18n/files.js
index b1253b4279..16694a9aca 100644
--- a/generators/entity-i18n/files.js
+++ b/generators/entity-i18n/files.js
@@ -32,7 +32,11 @@ function writeFiles() {
         writeEnumFiles() {
             this.fields.forEach(field => {
                 if (field.fieldIsEnum === true) {
-                    const enumInfo = utils.buildEnumInfo(field, this.angularAppName, this.packageName, this.clientRootFolder);
+                    const enumInfo = {
+                        ...utils.getEnumInfo(field, this.clientRootFolder),
+                        angularAppName: this.angularAppName,
+                        packageName: this.packageName
+                    };
 
                     // Copy for each
                     if (!this.skipClient && this.enableTranslation) {
diff --git a/generators/entity-i18n/templates/i18n/enum.json.ejs b/generators/entity-i18n/templates/i18n/enum.json.ejs
index 616b166941..f3a78f0f7c 100644
--- a/generators/entity-i18n/templates/i18n/enum.json.ejs
+++ b/generators/entity-i18n/templates/i18n/enum.json.ejs
@@ -19,8 +19,8 @@
 {
     "<%= angularAppName %>": {
         "<%= enumName %>" : {
-            "null": ""<% for (enumId in enums) { %>,
-            "<%= enums[enumId] %>": "<%= enums[enumId] %>"<% } %>
+            "null": ""<% enumValues.forEach(enumValue => { %>,
+            "<%= enumValue.name %>": "<%= enumValue.value %>"<% }); %>
         }
     }
 }
diff --git a/generators/entity-server/files.js b/generators/entity-server/files.js
index 38dd45262d..0d7a36e81c 100644
--- a/generators/entity-server/files.js
+++ b/generators/entity-server/files.js
@@ -386,20 +386,27 @@ function writeFiles() {
 
         writeEnumFiles() {
             this.fields.forEach(field => {
-                if (field.fieldIsEnum === true) {
-                    const fieldType = field.fieldType;
-                    const enumInfo = utils.buildEnumInfo(field, this.angularAppName, this.packageName, this.clientRootFolder);
-                    if (!this.skipServer) {
-                        this.template(
-                            `${this.fetchFromInstalledJHipster(
-                                'entity-server/templates'
-                            )}/${SERVER_MAIN_SRC_DIR}package/domain/enumeration/Enum.java.ejs`,
-                            `${SERVER_MAIN_SRC_DIR}${this.packageFolder}/domain/enumeration/${fieldType}.java`,
-                            this,
-                            {},
-                            enumInfo
-                        );
-                    }
+                if (!field.fieldIsEnum) {
+                    return;
+                }
+                const fieldType = field.fieldType;
+                const enumInfo = {
+                    ...utils.getEnumInfo(field, this.clientRootFolder),
+                    angularAppName: this.angularAppName,
+                    packageName: this.packageName
+                };
+                // eslint-disable-next-line no-console
+                if (!this.skipServer) {
+                    const pathToTemplateFile = `${this.fetchFromInstalledJHipster(
+                        'entity-server/templates'
+                    )}/${SERVER_MAIN_SRC_DIR}package/domain/enumeration/Enum.java.ejs`;
+                    this.template(
+                        pathToTemplateFile,
+                        `${SERVER_MAIN_SRC_DIR}${this.packageFolder}/domain/enumeration/${fieldType}.java`,
+                        this,
+                        {},
+                        enumInfo
+                    );
                 }
             });
         }
diff --git a/generators/entity-server/templates/src/main/java/package/domain/enumeration/Enum.java.ejs b/generators/entity-server/templates/src/main/java/package/domain/enumeration/Enum.java.ejs
index 00a9f3d294..50ba9ba8e2 100644
--- a/generators/entity-server/templates/src/main/java/package/domain/enumeration/Enum.java.ejs
+++ b/generators/entity-server/templates/src/main/java/package/domain/enumeration/Enum.java.ejs
@@ -22,5 +22,29 @@ package <%= packageName %>.domain.enumeration;
  * The <%= enumName %> enumeration.
  */
 public enum <%= enumName %> {
-    <%= enumValues %>
+<%_ if (withoutCustomValues) { _%>
+    <%= enumValues.map(enumValue => enumValue.name).join(', ') %>
+<%_ } else {
+    enumValues.forEach((enumWithCustomValue, index) => {
+        if (enumWithCustomValue.name === enumWithCustomValue.value) { _%>
+    <%= enumWithCustomValue.name %><% if (index < enumValues.length - 1) { %>,<% } else { %>;<% } %>
+        <%_ } else { _%>
+    <%= enumWithCustomValue.name %>("<%= enumWithCustomValue.value %>")<% if (index < enumValues.length - 1) { %>,<% } else { %>;<% } %>
+        <%_ }
+    }); _%>
+
+    private<% if (withCustomValues) { %> final<% } %> String value;
+
+<%_ if (withSomeCustomValues) { _%>
+    <%= enumName %>() {}
+<%_ } _%>
+
+    <%= enumName %>(String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+<%_ } _%>
 }
diff --git a/generators/entity-server/templates/src/main/resources/config/liquibase/fake-data/table.csv.ejs b/generators/entity-server/templates/src/main/resources/config/liquibase/fake-data/table.csv.ejs
index 2738acc4e2..b8542479ee 100644
--- a/generators/entity-server/templates/src/main/resources/config/liquibase/fake-data/table.csv.ejs
+++ b/generators/entity-server/templates/src/main/resources/config/liquibase/fake-data/table.csv.ejs
@@ -138,8 +138,8 @@ for (lineNb = 1; lineNb <= numberOfRows; lineNb++) {
         }
         if (fields[idx].fieldIsEnum) {
             if (fields[idx].fieldValues.length !== 0) {
-                const enums = fields[idx].fieldValues.split(',');
-                data = enums[this.faker.random.number(enums.length-1)];
+                const enumValues = getEnumValuesWithCustomValues(fields[idx].fieldValues);
+                data = enumValues[this.faker.random.number(enumValues.length-1)].name;
             } else {
                 data = '';
             }
diff --git a/generators/entity/index.js b/generators/entity/index.js
index 9fd039d294..ed50f4ed67 100644
--- a/generators/entity/index.js
+++ b/generators/entity/index.js
@@ -379,9 +379,6 @@ class EntityGenerator extends BaseBlueprintGenerator {
         return {
             validateFile() {
                 const context = this.context;
-                if (!context.useConfigurationFile) {
-                    return;
-                }
                 const entityName = context.name;
                 // Validate entity json field content
                 context.fields.forEach(field => {
diff --git a/generators/generator-base.js b/generators/generator-base.js
index 6eee3cbef6..830f3cbcae 100644
--- a/generators/generator-base.js
+++ b/generators/generator-base.js
@@ -2177,4 +2177,26 @@ module.exports = class extends PrivateBase {
         }
         return this._needleApi;
     }
+
+    /**
+     * From an enum's values (with or without custom values), returns the enum's values without custom values.
+     * @param {String} enumValues - an enum's values.
+     * @return {Array<String>} the formatted enum's values.
+     */
+    getEnumValuesWithCustomValues(enumValues) {
+        if (!enumValues || enumValues === '') {
+            throw new Error('Enumeration values must be passed to get the formatted values.');
+        }
+        return enumValues.split(',').map(enumValue => {
+            if (!enumValue.includes('(')) {
+                return { name: enumValue.trim(), value: enumValue.trim() };
+            }
+            // eslint-disable-next-line no-unused-vars
+            const matched = /\s*(.+?)\s*\((.+?)\)/.exec(enumValue);
+            return {
+                name: matched[1],
+                value: matched[2]
+            };
+        });
+    }
 };
diff --git a/generators/server/templates/src/main/java/package/config/CacheConfiguration.java.ejs b/generators/server/templates/src/main/java/package/config/CacheConfiguration.java.ejs
index 985025d916..d72e3ce016 100644
--- a/generators/server/templates/src/main/java/package/config/CacheConfiguration.java.ejs
+++ b/generators/server/templates/src/main/java/package/config/CacheConfiguration.java.ejs
@@ -101,6 +101,7 @@ import javax.annotation.PreDestroy;
 import org.infinispan.configuration.cache.CacheMode;
 import org.infinispan.configuration.cache.ConfigurationBuilder;
 import org.infinispan.configuration.global.GlobalConfigurationBuilder;
+import org.infinispan.jboss.marshalling.core.JBossUserMarshaller;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import io.github.jhipster.config.JHipsterProperties;
@@ -437,6 +438,7 @@ public class CacheConfiguration {
                     .addProperty("configurationFile", jHipsterProperties.getCache().getInfinispan().getConfigFile())
                     .clusterName("infinispan-<%= baseName %>-cluster")
                     .jmx().enabled(jHipsterProperties.getCache().getInfinispan().isStatsEnabled())
+                    .serialization().marshaller(new JBossUserMarshaller())
                     .build();
             } else if (discoveryClient.getInstances(registration.getServiceId()).size() == 0) {
                 return () -> GlobalConfigurationBuilder
@@ -444,6 +446,7 @@ public class CacheConfiguration {
                     .transport(new JGroupsTransport())
                     .clusterName("infinispan-<%= baseName %>-cluster")
                     .jmx().enabled(jHipsterProperties.getCache().getInfinispan().isStatsEnabled())
+                    .serialization().marshaller(new JBossUserMarshaller())
                     .build();
             } else {
                 return () -> GlobalConfigurationBuilder
@@ -451,6 +454,7 @@ public class CacheConfiguration {
                     .transport(new JGroupsTransport(getTransportChannel()))
                     .clusterName("infinispan-<%= baseName %>-cluster")
                     .jmx().enabled(jHipsterProperties.getCache().getInfinispan().isStatsEnabled())
+                    .serialization().marshaller(new JBossUserMarshaller())
                     .build();
             }
         <%_ } else { _%>
@@ -459,6 +463,7 @@ public class CacheConfiguration {
                     .addProperty("configurationFile", jHipsterProperties.getCache().getInfinispan().getConfigFile())
                     .clusterName("infinispan-<%= baseName %>-cluster")
                     .jmx().enabled(jHipsterProperties.getCache().getInfinispan().isStatsEnabled())
+                    .serialization().marshaller(new JBossUserMarshaller())
                     .build();
         <%_ } _%>
     }
diff --git a/generators/utils.js b/generators/utils.js
index 81c1ae0000..69e549e18f 100644
--- a/generators/utils.js
+++ b/generators/utils.js
@@ -56,6 +56,7 @@ module.exports = {
     deepFind,
     getJavadoc,
     buildEnumInfo,
+    getEnumInfo,
     copyObjectProps,
     decodeBase64,
     getAllJhipsterConfig,
@@ -453,6 +454,29 @@ function getJavadoc(text, indentSize) {
 
 /**
  * Build an enum object
+ * @param {Object} field - entity field
+ * @param {String} clientRootFolder - the client's root folder
+ * @return {Object} the enum info.
+ */
+function getEnumInfo(field, clientRootFolder) {
+    const fieldType = field.fieldType;
+    // Todo: check if the next line does a side-effect and refactor it.
+    field.enumInstance = _.lowerFirst(fieldType);
+    const enums = field.fieldValues.split(',').map(fieldValue => fieldValue.trim());
+    const customValuesState = getCustomValuesState(enums);
+    return {
+        enumName: fieldType,
+        enumInstance: field.enumInstance,
+        enums,
+        ...customValuesState,
+        enumValues: getEnums(enums, customValuesState),
+        clientRootFolder: clientRootFolder ? `${clientRootFolder}-` : ''
+    };
+}
+
+/**
+ * @Deprecated
+ * Build an enum object, deprecated use getEnumInfoInstead
  * @param {any} field : entity field
  * @param {string} angularAppName
  * @param {string} packageName
@@ -475,6 +499,12 @@ function buildEnumInfo(field, angularAppName, packageName, clientRootFolder) {
     };
 }
 
+/**
+ * @deprecated
+ * private function to remove for jhipster v7
+ * @param enums
+ * @return {*}
+ */
 function getEnumsWithCustomValue(enums) {
     return enums.reduce((enumsWithCustomValueArray, currentEnumValue) => {
         if (doesTheEnumValueHaveACustomValue(currentEnumValue)) {
@@ -489,6 +519,42 @@ function getEnumsWithCustomValue(enums) {
     }, []);
 }
 
+function getCustomValuesState(enumValues) {
+    const state = {
+        withoutCustomValue: 0,
+        withCustomValue: 0
+    };
+    enumValues.forEach(enumValue => {
+        if (doesTheEnumValueHaveACustomValue(enumValue)) {
+            state.withCustomValue++;
+        } else {
+            state.withoutCustomValue++;
+        }
+    });
+    return {
+        withoutCustomValues: state.withCustomValue === 0,
+        withSomeCustomValues: state.withCustomValue !== 0 && state.withoutCustomValue !== 0,
+        withCustomValues: state.withoutCustomValue === 0
+    };
+}
+
+function getEnums(enums, customValuesState) {
+    if (customValuesState.withoutCustomValues) {
+        return enums.map(enumValue => ({ name: enumValue, value: enumValue }));
+    }
+    return enums.map(enumValue => {
+        if (!doesTheEnumValueHaveACustomValue(enumValue)) {
+            return { name: enumValue.trim(), value: enumValue.trim() };
+        }
+        // eslint-disable-next-line no-unused-vars
+        const matched = /\s*(.+?)\s*\((.+?)\)/.exec(enumValue);
+        return {
+            name: matched[1],
+            value: matched[2]
+        };
+    });
+}
+
 function doesTheEnumValueHaveACustomValue(enumValue) {
     return enumValue.includes('(');
 }


Test patch:
diff --git a/generators/entity-client/templates/angular/src/test/javascript/spec/app/entities/entity-management.service.spec.ts.ejs b/generators/entity-client/templates/angular/src/test/javascript/spec/app/entities/entity-management.service.spec.ts.ejs
index 4a147cb8bb..7f3134c2e6 100644
--- a/generators/entity-client/templates/angular/src/test/javascript/spec/app/entities/entity-management.service.spec.ts.ejs
+++ b/generators/entity-client/templates/angular/src/test/javascript/spec/app/entities/entity-management.service.spec.ts.ejs
@@ -68,7 +68,7 @@ describe('Service Tests', () => {
                 const fieldType = field.fieldType;
             _%>
                 <%_ if (field.fieldIsEnum) { _%>
-                <%= fieldType + '.' + field.fieldValues.split(',')[0].replace(/(.+?)\(.*/, (match, firstGroup) => firstGroup) %>,
+                <%= fieldType + '.' + getEnumValuesWithCustomValues(field.fieldValues)[0].name %>,
                 <%_ } else if (fieldType === 'Boolean') { _%>
                 false,
                 <%_ } else if (['Integer', 'Long', 'Float', 'Double', 'BigDecimal', 'Duration'].includes(fieldType)) { _%>
diff --git a/generators/entity-server/templates/src/test/gatling/user-files/simulations/EntityGatlingTest.scala.ejs b/generators/entity-server/templates/src/test/gatling/user-files/simulations/EntityGatlingTest.scala.ejs
index 8c17ae2073..ee4b392fe2 100644
--- a/generators/entity-server/templates/src/test/gatling/user-files/simulations/EntityGatlingTest.scala.ejs
+++ b/generators/entity-server/templates/src/test/gatling/user-files/simulations/EntityGatlingTest.scala.ejs
@@ -188,7 +188,7 @@ class <%= entityClass %>GatlingTest extends Simulation {
             .body(StringBody("""{
                 "id":null
                 <%_ for (idx in fields) { _%>
-                , "<%= fields[idx].fieldName %>":<% if (fields[idx].fieldType === 'String') { %>"SAMPLE_TEXT"<% } else if (['Integer', 'BigDecimal'].includes(fields[idx].fieldType)) { %>"0"<% } else if (['Instant', 'ZonedDateTime', 'LocalDate'].includes(fields[idx].fieldType)) { %>"2020-01-01T00:00:00.000Z"<% } else if (fields[idx].fieldIsEnum) { %>"<%= fields[idx].fieldValues.split(',')[0] %>"<% } else { %>null<% } %>
+                , "<%= fields[idx].fieldName %>":<% if (fields[idx].fieldType === 'String') { %>"SAMPLE_TEXT"<% } else if (['Integer', 'BigDecimal'].includes(fields[idx].fieldType)) { %>"0"<% } else if (['Instant', 'ZonedDateTime', 'LocalDate'].includes(fields[idx].fieldType)) { %>"2020-01-01T00:00:00.000Z"<% } else if (fields[idx].fieldIsEnum) { %>"<%= getEnumValuesWithCustomValues(fields[idx].fieldValues)[0].name %>"<% } else { %>null<% } %>
                 <%_ } _%>
                 }""")).asJson
             .check(status.is(201))
diff --git a/generators/entity-server/templates/src/test/java/package/web/rest/EntityResourceIT.java.ejs b/generators/entity-server/templates/src/test/java/package/web/rest/EntityResourceIT.java.ejs
index ddb1efa7b6..55e657f55b 100644
--- a/generators/entity-server/templates/src/test/java/package/web/rest/EntityResourceIT.java.ejs
+++ b/generators/entity-server/templates/src/test/java/package/web/rest/EntityResourceIT.java.ejs
@@ -239,10 +239,10 @@ public class <%= entityClass %>ResourceIT <% if (databaseType === 'cassandra') {
     let enumValue1;
     let enumValue2;
     if (isEnum) {
-        const values = fields[idx].fieldValues.replace(/\s/g, '').split(',');
-        enumValue1 = values[0];
-        if (values.length > 1) {
-            enumValue2 = values[1];
+        const enumValues = getEnumValuesWithCustomValues(fields[idx].fieldValues);
+        enumValue1 = enumValues[0];
+        if (enumValues.length > 1) {
+            enumValue2 = enumValues[1];
         } else {
             enumValue2 = enumValue1;
         }
@@ -374,8 +374,8 @@ public class <%= entityClass %>ResourceIT <% if (databaseType === 'cassandra') {
     private static final String <%= updatedValueName %>_CONTENT_TYPE = "image/png";
     <%_ } else if (isEnum) { _%>
 
-    private static final <%= fieldType %> <%= defaultValueName %> = <%= fieldType %>.<%= enumValue1 %>;
-    private static final <%= fieldType %> <%= updatedValueName %> = <%= fieldType %>.<%= enumValue2 %>;
+    private static final <%= fieldType %> <%= defaultValueName %> = <%= fieldType %>.<%= enumValue1.name %>;
+    private static final <%= fieldType %> <%= updatedValueName %> = <%= fieldType %>.<%= enumValue2.name %>;
     <%_ } } _%>
 
     @Autowired
diff --git a/test-integration/samples/.jhipster/FieldTestEnumWithValue.json b/test-integration/samples/.jhipster/FieldTestEnumWithValue.json
new file mode 100644
index 0000000000..0a1ebfb5c7
--- /dev/null
+++ b/test-integration/samples/.jhipster/FieldTestEnumWithValue.json
@@ -0,0 +1,32 @@
+{
+    "name": "FieldTestEnumWithValue",
+    "fields": [
+        {
+            "fieldName": "myFieldA",
+            "fieldType": "MyEnumA",
+            "fieldValues": "AAA,BBB"
+        },
+        {
+            "fieldName": "myFieldB",
+            "fieldType": "MyEnumB",
+            "fieldValues": "AAA (aaa_aaa),BBB"
+        },
+        {
+            "fieldName": "myFieldC",
+            "fieldType": "MyEnumC",
+            "fieldValues": "AAA (aaa_aaa),BBB (b and b)"
+        }
+    ],
+    "relationships": [],
+    "changelogDate": "20200415193919",
+    "entityTableName": "entity_with_enums",
+    "dto": "no",
+    "pagination": "no",
+    "service": "no",
+    "jpaMetamodelFiltering": false,
+    "fluentMethods": true,
+    "readOnly": false,
+    "embedded": false,
+    "clientRootFolder": "",
+    "applications": "*"
+}
diff --git a/test-integration/samples/jdl-default/app.jdl b/test-integration/samples/jdl-default/app.jdl
index 6b8fc66f8e..c5e73547db 100644
--- a/test-integration/samples/jdl-default/app.jdl
+++ b/test-integration/samples/jdl-default/app.jdl
@@ -40,9 +40,9 @@ entity Operation {
 }
 
 enum BankAccountType {
-  CHECKING,
-  SAVINGS,
-  LOAN
+  CHECKING (checking_account),
+  SAVINGS (savings_account),
+  LOAN (loan_account)
 }
 
 entity Department {
diff --git a/test-integration/scripts/11-generate-entities.sh b/test-integration/scripts/11-generate-entities.sh
index 68e8dc75c9..b227733348 100755
--- a/test-integration/scripts/11-generate-entities.sh
+++ b/test-integration/scripts/11-generate-entities.sh
@@ -100,6 +100,7 @@ elif [[ "$JHI_ENTITY" == "sqlfull" ]]; then
     moveEntity FieldTestServiceImplEntity
     moveEntity FieldTestInfiniteScrollEntity
     moveEntity FieldTestPaginationEntity
+    moveEntity FieldTestEnumWithValue
 
     moveEntity TestEntity
     moveEntity TestMapstruct
@@ -140,6 +141,7 @@ elif [[ "$JHI_ENTITY" == "sql" ]]; then
     moveEntity FieldTestServiceImplEntity
     moveEntity FieldTestInfiniteScrollEntity
     moveEntity FieldTestPaginationEntity
+    moveEntity FieldTestEnumWithValue
 
     moveEntity EntityWithDTO
     moveEntity EntityWithPaginationAndDTO
diff --git a/test/entity.spec.js b/test/entity.spec.js
index 24c14e71dd..79eff3506a 100644
--- a/test/entity.spec.js
+++ b/test/entity.spec.js
@@ -850,5 +850,76 @@ describe('JHipster generator for entity', () => {
                 });
             });
         });
+
+        context('when generating enums', () => {
+            let enumWithoutCustomValuesPath;
+            let enumWithSomeCustomValuesPath;
+            let enumWithOnlyCustomValuesPath;
+
+            before(done => {
+                enumWithoutCustomValuesPath = `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/domain/enumeration/MyEnumA.java`;
+                enumWithSomeCustomValuesPath = `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/domain/enumeration/MyEnumB.java`;
+                enumWithOnlyCustomValuesPath = `${SERVER_MAIN_SRC_DIR}com/mycompany/myapp/domain/enumeration/MyEnumC.java`;
+                helpers
+                    .run(require.resolve('../generators/entity'))
+                    .inTmpDir(dir => {
+                        fse.copySync(path.join(__dirname, '../test/templates/enums'), dir);
+                    })
+                    .withArguments(['A'])
+                    .on('end', done);
+            });
+
+            context('for enum content without custom values', () => {
+                it('generates the java enum content', () => {
+                    assert.fileContent(enumWithoutCustomValuesPath, /AAA,\s+BBB/);
+                });
+                it('does not generate a private field', () => {
+                    assert.noFileContent(enumWithoutCustomValuesPath, /private final String value;/);
+                });
+                it('does not generate an empty constructor', () => {
+                    assert.noFileContent(enumWithoutCustomValuesPath, /MyEnumA\(\)/);
+                });
+                it('does not generate a non-empty constructor', () => {
+                    assert.noFileContent(enumWithoutCustomValuesPath, /MyEnumA\(String value\)/);
+                });
+                it('does not generate a getter for the value', () => {
+                    assert.noFileContent(enumWithoutCustomValuesPath, /public String getValue\(\)/);
+                });
+            });
+            context('for enum content with some custom values', () => {
+                it('generates the java enum content', () => {
+                    assert.fileContent(enumWithSomeCustomValuesPath, /AAA\("aaa_aaa"\),\s+BBB;/);
+                });
+                it('generates a non-final private field', () => {
+                    assert.fileContent(enumWithSomeCustomValuesPath, /private String value;/);
+                });
+                it('generates an empty constructor', () => {
+                    assert.fileContent(enumWithSomeCustomValuesPath, /MyEnumB\(\)/);
+                });
+                it('generates a non-empty constructor', () => {
+                    assert.fileContent(enumWithSomeCustomValuesPath, /MyEnumB\(String value\)/);
+                });
+                it('generates a getter for the value', () => {
+                    assert.fileContent(enumWithSomeCustomValuesPath, /public String getValue\(\)/);
+                });
+            });
+            context('for enum content with only custom values', () => {
+                it('generates the java enum content', () => {
+                    assert.fileContent(enumWithOnlyCustomValuesPath, /AAA\("aaa_aaa"\),\s+BBB\("bbb"\);/);
+                });
+                it('generates a final private field', () => {
+                    assert.fileContent(enumWithOnlyCustomValuesPath, /private final String value;/);
+                });
+                it('does not generate an empty constructor', () => {
+                    assert.noFileContent(enumWithOnlyCustomValuesPath, /MyEnumC\(\)/);
+                });
+                it('generates a non-empty constructor', () => {
+                    assert.fileContent(enumWithOnlyCustomValuesPath, /MyEnumC\(String value\)/);
+                });
+                it('generates a getter for the value', () => {
+                    assert.fileContent(enumWithOnlyCustomValuesPath, /public String getValue\(\)/);
+                });
+            });
+        });
     });
 });
diff --git a/test/generator-base.spec.js b/test/generator-base.spec.js
index d56c744fba..e530ddf2a9 100644
--- a/test/generator-base.spec.js
+++ b/test/generator-base.spec.js
@@ -268,7 +268,6 @@ describe('Generator Base', () => {
             });
         });
     });
-
     describe('writeFilesToDisk', () => {
         describe('when called with default angular client options', () => {
             it('should produce correct files', () => {
@@ -304,4 +303,47 @@ describe('Generator Base', () => {
             });
         });
     });
+    describe('getEnumValuesWithCustomValues', () => {
+        describe('when not passing anything', () => {
+            it('should fail', () => {
+                expect(() => BaseGenerator.getEnumValuesWithCustomValues()).to.throw(
+                    /^Enumeration values must be passed to get the formatted values\.$/
+                );
+            });
+        });
+        describe('when passing an empty string', () => {
+            it('should fail', () => {
+                expect(() => BaseGenerator.getEnumValuesWithCustomValues('')).to.throw(
+                    /^Enumeration values must be passed to get the formatted values\.$/
+                );
+            });
+        });
+        describe('when passing a string without custom enum values', () => {
+            it('should return a formatted list', () => {
+                expect(BaseGenerator.getEnumValuesWithCustomValues('FRANCE, ENGLAND, ICELAND')).to.deep.equal([
+                    { name: 'FRANCE', value: 'FRANCE' },
+                    { name: 'ENGLAND', value: 'ENGLAND' },
+                    { name: 'ICELAND', value: 'ICELAND' }
+                ]);
+            });
+        });
+        describe('when passing a string with some custom enum values', () => {
+            it('should return a formatted list', () => {
+                expect(BaseGenerator.getEnumValuesWithCustomValues('FRANCE(france), ENGLAND, ICELAND (viking_country)')).to.deep.equal([
+                    { name: 'FRANCE', value: 'france' },
+                    { name: 'ENGLAND', value: 'ENGLAND' },
+                    { name: 'ICELAND', value: 'viking_country' }
+                ]);
+            });
+        });
+        describe('when passing a string custom enum values for each value', () => {
+            it('should return a formatted list', () => {
+                expect(BaseGenerator.getEnumValuesWithCustomValues('FRANCE(france), ENGLAND(england), ICELAND (iceland)')).to.deep.equal([
+                    { name: 'FRANCE', value: 'france' },
+                    { name: 'ENGLAND', value: 'england' },
+                    { name: 'ICELAND', value: 'iceland' }
+                ]);
+            });
+        });
+    });
 });
diff --git a/test/templates/enums/.jhipster/A.json b/test/templates/enums/.jhipster/A.json
new file mode 100644
index 0000000000..ba5c0e8a8d
--- /dev/null
+++ b/test/templates/enums/.jhipster/A.json
@@ -0,0 +1,32 @@
+{
+    "name": "A",
+    "fields": [
+        {
+            "fieldName": "myFieldA",
+            "fieldType": "MyEnumA",
+            "fieldValues": "AAA,BBB"
+        },
+        {
+            "fieldName": "myFieldB",
+            "fieldType": "MyEnumB",
+            "fieldValues": "AAA (aaa_aaa),BBB"
+        },
+        {
+            "fieldName": "myFieldC",
+            "fieldType": "MyEnumC",
+            "fieldValues": "AAA (aaa_aaa),BBB (bbb)"
+        }
+    ],
+    "relationships": [],
+    "changelogDate": "20200415193919",
+    "entityTableName": "a",
+    "dto": "no",
+    "pagination": "no",
+    "service": "no",
+    "jpaMetamodelFiltering": false,
+    "fluentMethods": true,
+    "readOnly": false,
+    "embedded": false,
+    "clientRootFolder": "",
+    "applications": "*"
+}
diff --git a/test/templates/enums/.yo-rc.json b/test/templates/enums/.yo-rc.json
new file mode 100644
index 0000000000..cc69990c3f
--- /dev/null
+++ b/test/templates/enums/.yo-rc.json
@@ -0,0 +1,28 @@
+{
+  "generator-jhipster": {
+    "applicationType": "monolith",
+    "baseName": "sampleMysql",
+    "packageName": "com.mycompany.myapp",
+    "packageFolder": "com/mycompany/myapp",
+    "authenticationType": "session",
+    "cacheProvider": "ehcache",
+    "enableHibernateCache": true,
+    "websocket": "no",
+    "databaseType": "sql",
+    "devDatabaseType": "h2Disk",
+    "prodDatabaseType": "mysql",
+    "searchEngine": "no",
+      "buildTool": "maven",
+    "enableTranslation": true,
+    "nativeLanguage": "en",
+    "languages": [
+      "en",
+      "fr"
+    ],
+    "rememberMeKey": "2bb60a80889aa6e6767e9ccd8714982681152aa5",
+    "clientFramework": "angularX",
+    "testFrameworks": [
+      "gatling"
+    ]
+  }
+}
diff --git a/test/utils.spec.js b/test/utils.spec.js
index cf555fa51b..69608cf223 100644
--- a/test/utils.spec.js
+++ b/test/utils.spec.js
@@ -47,14 +47,145 @@ describe('JHipster Utils', () => {
             assert.objectContent(dst, src);
         });
     });
-    describe('::buildEnumFunction', () => {
-        it('describes all the properties of the entity', () => {
-            const packageName = 'com.package';
-            const angularAppName = 'myApp';
-            const clientRootFolder = 'root';
-            const entity = { enumName: 'entityName', fieldValues: 'field1, field2' };
-            const infos = utils.buildEnumInfo(entity, angularAppName, packageName, clientRootFolder);
-            assert.objectContent(infos, { packageName, angularAppName, clientRootFolder: `${clientRootFolder}-` });
+    describe('::getEnumInfo', () => {
+        describe('when passing field data', () => {
+            let enumInfo;
+
+            before(() => {
+                const clientRootFolder = 'root';
+                const field = { enumName: 'fieldName', fieldType: 'BigLetters', fieldValues: 'AAA, BBB' };
+                enumInfo = utils.getEnumInfo(field, clientRootFolder);
+            });
+
+            it("returns the enum's name", () => {
+                assert.strictEqual(enumInfo.enumName, 'BigLetters');
+            });
+            it("returns the enum's instance", () => {
+                assert.strictEqual(enumInfo.enumInstance, 'bigLetters');
+            });
+            it('returns the enums values', () => {
+                assert.deepStrictEqual(enumInfo.enums, ['AAA', 'BBB']);
+            });
+        });
+        describe("when the enums don't have custom values", () => {
+            let enumInfo;
+
+            before(() => {
+                const clientRootFolder = 'root';
+                const field = { enumName: 'fieldName', fieldValues: 'AAA, BBB' };
+                enumInfo = utils.getEnumInfo(field, clientRootFolder);
+            });
+
+            it('returns whether there are custom enums', () => {
+                assert.strictEqual(enumInfo.withoutCustomValues, true);
+                assert.strictEqual(enumInfo.withSomeCustomValues, false);
+                assert.strictEqual(enumInfo.withCustomValues, false);
+            });
+            it('returns the enums values', () => {
+                assert.deepStrictEqual(enumInfo.enumValues, [
+                    { name: 'AAA', value: 'AAA' },
+                    { name: 'BBB', value: 'BBB' }
+                ]);
+            });
+        });
+        describe('when some enums have custom values', () => {
+            let enumInfo;
+
+            before(() => {
+                const clientRootFolder = 'root';
+                const field = { enumName: 'fieldName', fieldValues: 'AAA(aaa), BBB' };
+                enumInfo = utils.getEnumInfo(field, clientRootFolder);
+            });
+
+            it('returns whether there are custom enums', () => {
+                assert.strictEqual(enumInfo.withoutCustomValues, false);
+                assert.strictEqual(enumInfo.withSomeCustomValues, true);
+                assert.strictEqual(enumInfo.withCustomValues, false);
+            });
+            it('returns the enums values', () => {
+                assert.deepStrictEqual(enumInfo.enumValues, [
+                    {
+                        name: 'AAA',
+                        value: 'aaa'
+                    },
+                    { name: 'BBB', value: 'BBB' }
+                ]);
+            });
+        });
+        describe('when all the enums have custom values', () => {
+            describe('without spaces inside them', () => {
+                let enumInfo;
+
+                before(() => {
+                    const clientRootFolder = 'root';
+                    const field = { enumName: 'fieldName', fieldValues: 'AAA(aaa), BBB(bbb)' };
+                    enumInfo = utils.getEnumInfo(field, clientRootFolder);
+                });
+
+                it('returns whether there are custom enums', () => {
+                    assert.strictEqual(enumInfo.withoutCustomValues, false);
+                    assert.strictEqual(enumInfo.withSomeCustomValues, false);
+                    assert.strictEqual(enumInfo.withCustomValues, true);
+                });
+                it('returns the enums values', () => {
+                    assert.deepStrictEqual(enumInfo.enumValues, [
+                        {
+                            name: 'AAA',
+                            value: 'aaa'
+                        },
+                        { name: 'BBB', value: 'bbb' }
+                    ]);
+                });
+            });
+            describe('with spaces inside them', () => {
+                let enumInfo;
+
+                before(() => {
+                    const clientRootFolder = 'root';
+                    const field = { enumName: 'fieldName', fieldValues: 'AAA(aaa), BBB(bbb and b)' };
+                    enumInfo = utils.getEnumInfo(field, clientRootFolder);
+                });
+
+                it('returns whether there are custom enums', () => {
+                    assert.strictEqual(enumInfo.withoutCustomValues, false);
+                    assert.strictEqual(enumInfo.withSomeCustomValues, false);
+                    assert.strictEqual(enumInfo.withCustomValues, true);
+                });
+                it('returns the enums values', () => {
+                    assert.deepStrictEqual(enumInfo.enumValues, [
+                        {
+                            name: 'AAA',
+                            value: 'aaa'
+                        },
+                        { name: 'BBB', value: 'bbb and b' }
+                    ]);
+                });
+            });
+        });
+        describe('when not passing a client root folder', () => {
+            let enumInfo;
+
+            before(() => {
+                const field = { enumName: 'fieldName', fieldValues: 'AAA, BBB' };
+                enumInfo = utils.getEnumInfo(field);
+            });
+
+            it('returns an empty string for the clientRootFolder property', () => {
+                assert.strictEqual(enumInfo.clientRootFolder, '');
+            });
+        });
+        describe('when passing a client root folder', () => {
+            let enumInfo;
+
+            before(() => {
+                const field = { enumName: 'fieldName', fieldValues: 'AAA, BBB' };
+                const clientRootFolder = 'root';
+                enumInfo = utils.getEnumInfo(field, clientRootFolder);
+            });
+
+            it('returns the clientRootFolder property suffixed by a dash', () => {
+                assert.strictEqual(enumInfo.clientRootFolder, 'root-');
+            });
         });
     });
     describe('::deepFind function', () => {