Skip to content

Commit

Permalink
Issue OpenLiberty#28911: Intermittent java:global NameNotFoundException
Browse files Browse the repository at this point in the history
Update injection scope code that handles deferred processing for java:global and java:app
to properly handle concurrent threads looking up JNDI names in these contexts.

Previously, the first thread would begin processing the deferred metadata and the second thread
would then assume it was already complete and fail. The change will now result in the second
thread waiting on a Future for the first thread to complete.

Also updated Jakart Data code to report this failure immediately, rather than ignoring it
until the failure potentially occurs at a later time.
  • Loading branch information
tkburroughs committed Jun 26, 2024
1 parent 092bf09 commit 02a3bd0
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*******************************************************************************
* Copyright (c) 2011, 2019 IBM Corporation and others.
* Copyright (c) 2011, 2024 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-2.0/
*
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
Expand All @@ -23,6 +23,8 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
Expand Down Expand Up @@ -130,6 +132,15 @@ public class OSGiInjectionScopeData extends InjectionScopeData implements Deferr
*/
private Map<DeferredReferenceData, Boolean> deferredReferenceDatas;

/**
* The result of {@link #processDeferredReferenceData} for the current list of reference
* contexts that are registered for deferred processing if a non-java:comp request is made.
* This field is initialized lazily and cleared when no longer needed.
*
* @see #deferredReferenceDatas
*/
private CompletableFuture<Boolean> deferredReferenceDatasFuture;

public OSGiInjectionScopeData(J2EEName j2eeName, NamingConstants.JavaColonNamespace namespace, OSGiInjectionScopeData parent, ReentrantReadWriteLock nonCompEnvLock) {
super(j2eeName);
this.namespace = namespace;
Expand Down Expand Up @@ -515,6 +526,8 @@ private synchronized void disableDeferredReferenceData() {
if (parent != null && deferredReferenceDatas != null) {
parent.removeDeferredReferenceData(this);
deferredReferenceDatas = null;
deferredReferenceDatasFuture.complete(false);
deferredReferenceDatasFuture = null;
}
}

Expand All @@ -528,6 +541,7 @@ public synchronized void addDeferredReferenceData(DeferredReferenceData refData)

if (deferredReferenceDatas == null) {
deferredReferenceDatas = new LinkedHashMap<DeferredReferenceData, Boolean>();
deferredReferenceDatasFuture = new CompletableFuture<Boolean>();
if (parent != null && deferredReferenceDataEnabled) {
parent.addDeferredReferenceData(this);
}
Expand Down Expand Up @@ -563,18 +577,17 @@ public synchronized void removeDeferredReferenceData(DeferredReferenceData refDa
*/
@Override
public boolean processDeferredReferenceData() {
if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
Tr.entry(tc, "processDeferredReferenceData", "this=" + this);
final boolean isTraceOn = TraceComponent.isAnyTracingEnabled();
if (isTraceOn && tc.isEntryEnabled()) {
Tr.entry(tc, "processDeferredReferenceData", "this=" + this + " future=" + deferredReferenceDatasFuture);
}

Map<DeferredReferenceData, Boolean> deferredReferenceDatas;
CompletableFuture<Boolean> currentFuture;
synchronized (this) {
deferredReferenceDatas = this.deferredReferenceDatas;
this.deferredReferenceDatas = null;

if (parent != null) {
parent.removeDeferredReferenceData(this);
}
currentFuture = deferredReferenceDatasFuture;
}

boolean any = false;
Expand All @@ -589,12 +602,38 @@ public boolean processDeferredReferenceData() {
// (erroneous or conflicting metadata). Any exception that
// is thrown will be rethrown by ReferenceContext.process
// when the component is actually used.
ex.getClass(); // findbugs
if (isTraceOn && tc.isDebugEnabled())
Tr.debug(tc, "ignoring : " + ex);
}
}

synchronized (this) {
// Removed from parent after processing completes so concurrent access of parent
// will block on the parent future until all child processing completes.
if (parent != null) {
parent.removeDeferredReferenceData(this);
}

// Unblock any concurrent access and clear the future as is no longer required.
if (deferredReferenceDatasFuture != null) {
deferredReferenceDatasFuture.complete(any);
deferredReferenceDatasFuture = null;
}
}
} else if (currentFuture != null) {
try {
// Wait a reasonable amount of time for deferred processing to complete
if (isTraceOn && tc.isDebugEnabled())
Tr.debug(tc, "waiting up to 60 seconds for completion of " + currentFuture);
any = currentFuture.get(60, TimeUnit.SECONDS);
} catch (Exception e) {
if (isTraceOn && tc.isDebugEnabled())
Tr.debug(tc, "processDeferredReferenceData failed to complete; assuming something was processed");
any = true;
}
}

if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
if (isTraceOn && tc.isEntryEnabled()) {
Tr.exit(tc, "processDeferredReferenceData", any);
}
return any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
Expand All @@ -25,6 +26,7 @@
import com.ibm.websphere.ras.Tr;
import com.ibm.websphere.ras.TraceComponent;
import com.ibm.websphere.ras.annotation.Trivial;
import com.ibm.ws.ffdc.FFDCFilter;
import com.ibm.ws.ffdc.annotation.FFDCIgnore;
import com.ibm.ws.runtime.metadata.ComponentMetaData;
import com.ibm.ws.threadContext.ComponentMetaDataAccessorImpl;
Expand Down Expand Up @@ -150,6 +152,9 @@ EntityManagerBuilder createEMBuilder() {
resourceName, metadataIdentifier, application, module, component, entityTypes);

} catch (NamingException x) {
FFDCFilter.processException(x, this.getClass().getName() + ".createEMBuilder", "155", this, new Object[] { (switchMetadata ? repoMetadata : extMetadata) });
throw new CompletionException("Unable to find " + dataStore + " from " +
(switchMetadata ? repoMetadata : extMetadata).getJ2EEName(), x);
}
} else if (!DataExtension.DEFAULT_DATA_STORE.equals(resourceName)) {
// Check for resource references and persistence unit references where java:comp/env/ is omitted:
Expand All @@ -169,6 +174,8 @@ EntityManagerBuilder createEMBuilder() {
resourceName = javaCompName;
}
} catch (NamingException x) {
if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
Tr.debug(this, tc, "Ignoring lookup failure for " + javaCompName + " : " + x);
}
}
} finally {
Expand Down Expand Up @@ -214,12 +221,12 @@ public String toString() {
(application == null ? 4 : application.length()) +
(module == null ? 4 : module.length()) +
(component == null ? 4 : component.length())) //
.append("FutureEMBuilder@") //
.append(Integer.toHexString(hashCode())) //
.append(":").append(dataStore) //
.append(' ').append(application) //
.append('#').append(module) //
.append('#').append(component);
.append("FutureEMBuilder@") //
.append(Integer.toHexString(hashCode())) //
.append(":").append(dataStore) //
.append(' ').append(application) //
.append('#').append(module) //
.append('#').append(component);
return b.toString();
}
}

0 comments on commit 02a3bd0

Please sign in to comment.