Merge branch 'pr/448' into asf-master
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 2edf04d..5b821b1 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -19,12 +19,14 @@
 
 Bug Fixes:
 
+CAY-2661 NPE on DB Import screen
 CAY-2683 Don't use DISTINCT for joins on to-one related tables
 CAY-2685 JsonType should use the actual JDBC type, not OTHER
 CAY-2686 SQL translator incorrectly quotes fully qualified tables' names
 CAY-2687 Modeler Migrate Repeatedly Asks to Set Column Type for MySQL
 CAY-2690 dbimport skips length changes for BINARY and VARBINARY columns
 CAY-2691 MySQL driver 8.0.x stores LocalDateTime differently than 5.1.x
+CAY-2698 EventSubject.getSubject() is not thread safe
 
 ----------------------------------
 Release: 4.2.M2
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/EventSubject.java b/cayenne-server/src/main/java/org/apache/cayenne/event/EventSubject.java
index 99c8b43..7cc0fdc 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/EventSubject.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/event/EventSubject.java
@@ -40,8 +40,7 @@
 public class EventSubject implements Serializable {
 
     // a Map that will allow the values to be GC'ed
-    @SuppressWarnings("unchecked")
-    private static Map<String, EventSubject> _registeredSubjects = new WeakValueMap<>();
+    private static final Map<String, EventSubject> _registeredSubjects = new WeakValueMap<>();
 
     // Subject identifier in the form "com.foo.bar/SubjectName"
     private String _fullyQualifiedSubjectName;
@@ -64,10 +63,14 @@
         }
 
         String fullSubjectName = subjectOwner.getName() + "/" + subjectName;
-        EventSubject newSubject = _registeredSubjects.get(fullSubjectName);
-        if (newSubject == null) {
-            newSubject = new EventSubject(fullSubjectName);
-            _registeredSubjects.put(newSubject.getSubjectName(), newSubject);
+
+        EventSubject newSubject;
+        synchronized (_registeredSubjects) {
+            newSubject = _registeredSubjects.get(fullSubjectName);
+            if(newSubject == null) {
+                newSubject = new EventSubject(fullSubjectName);
+                _registeredSubjects.put(fullSubjectName, newSubject);
+            }
         }
 
         return newSubject;
@@ -92,8 +95,7 @@
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof EventSubject) {
-            return _fullyQualifiedSubjectName.equals(((EventSubject) obj)
-                    .getSubjectName());
+            return _fullyQualifiedSubjectName.equals(((EventSubject) obj).getSubjectName());
         }
 
         return false;
@@ -101,7 +103,9 @@
 
     @Override
     public int hashCode() {
-        return new HashCodeBuilder(17, 3).append(_fullyQualifiedSubjectName).toHashCode();
+        return new HashCodeBuilder(17, 3)
+                .append(_fullyQualifiedSubjectName)
+                .toHashCode();
     }
 
     public String getSubjectName() {
@@ -114,15 +118,7 @@
      */
     @Override
     public String toString() {
-        StringBuilder buf = new StringBuilder(64);
-
-        buf.append("<");
-        buf.append(this.getClass().getName());
-        buf.append(" 0x");
-        buf.append(Integer.toHexString(System.identityHashCode(this)));
-        buf.append("> ");
-        buf.append(_fullyQualifiedSubjectName);
-
-        return buf.toString();
+        return "<" + this.getClass().getName() + " 0x" + Integer.toHexString(System.identityHashCode(this)) + "> "
+                + _fullyQualifiedSubjectName;
     }
 }