Skip to content

Commit 1381dd5

Browse files
authored
Adds more classes as part of overall JPA refactoring effort (helidon-io#6584)
Signed-off-by: Laird Nelson <laird.nelson@oracle.com>
1 parent bbc170d commit 1381dd5

11 files changed

Lines changed: 3189 additions & 2 deletions

integrations/cdi/jpa-cdi/etc/spotbugs/exclude.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@
3131
<Class name="io.helidon.integrations.cdi.jpa.DelegatingTypedQuery"/>
3232
<Bug pattern="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT"/>
3333
</Match>
34+
<Match>
35+
<Class name="io.helidon.integrations.cdi.jpa.PersistenceExtension$ReferenceCountingProducer"/>
36+
<Bug pattern="UPM_UNCALLED_PRIVATE_METHOD"/>
37+
</Match>
3438
</FindBugsFilter>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2023 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.helidon.integrations.cdi.jpa;
17+
18+
import javax.sql.DataSource;
19+
20+
import jakarta.enterprise.inject.Instance;
21+
import jakarta.enterprise.inject.literal.NamedLiteral;
22+
import jakarta.inject.Inject;
23+
import jakarta.inject.Singleton;
24+
25+
@Singleton
26+
class JtaAbsentDataSourceProvider implements PersistenceUnitInfoBean.DataSourceProvider {
27+
28+
29+
/*
30+
* Instance fields.
31+
*/
32+
33+
34+
private final Instance<DataSource> instance;
35+
36+
37+
/*
38+
* Constructors.
39+
*/
40+
41+
42+
@Deprecated
43+
JtaAbsentDataSourceProvider() {
44+
this(null);
45+
}
46+
47+
@Inject
48+
JtaAbsentDataSourceProvider(Instance<DataSource> instance) {
49+
super();
50+
this.instance = instance;
51+
}
52+
53+
54+
/*
55+
* Instance methods.
56+
*/
57+
58+
59+
@Override
60+
public DataSource getDataSource(boolean jta, boolean useDefaultJta, String dataSourceName) {
61+
Instance<DataSource> instance = this.instance;
62+
if (dataSourceName == null) {
63+
if (useDefaultJta) {
64+
instance = null;
65+
}
66+
} else if (instance != null) {
67+
instance = instance.select(NamedLiteral.of(dataSourceName));
68+
}
69+
return instance == null ? null : instance.get();
70+
}
71+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* Copyright (c) 2023 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.helidon.integrations.cdi.jpa;
17+
18+
import java.util.Objects;
19+
import java.util.concurrent.ConcurrentHashMap;
20+
import java.util.concurrent.ConcurrentMap;
21+
22+
import javax.sql.DataSource;
23+
24+
import io.helidon.integrations.jta.jdbc.ExceptionConverter;
25+
import io.helidon.integrations.jta.jdbc.JtaAdaptingDataSource;
26+
27+
import jakarta.annotation.PreDestroy;
28+
import jakarta.enterprise.inject.Instance;
29+
import jakarta.enterprise.inject.literal.NamedLiteral;
30+
import jakarta.inject.Inject;
31+
import jakarta.inject.Singleton;
32+
import jakarta.transaction.TransactionManager;
33+
import jakarta.transaction.TransactionSynchronizationRegistry;
34+
35+
@Singleton
36+
final class JtaAdaptingDataSourceProvider implements PersistenceUnitInfoBean.DataSourceProvider {
37+
38+
39+
/*
40+
* Static fields.
41+
*/
42+
43+
44+
/**
45+
* A token to use as a key in the {@link #jtaDataSourcesByName} field value for a data source name when the real
46+
* data source name is {@code null}.
47+
*
48+
* <p>Real data source names can be {@code null} and the empty string ({@code ""}), so a different value is used
49+
* here.</p>
50+
*
51+
* <p>This field is never {@code null}.</p>
52+
*/
53+
private static final String NULL_DATASOURCE_NAME = "\u0000";
54+
55+
56+
/*
57+
* Instance fields.
58+
*/
59+
60+
61+
private final ConcurrentMap<String, JtaAdaptingDataSource> jtaDataSourcesByName;
62+
63+
/**
64+
* An {@link Instance} providing access to CDI contextual references.
65+
*
66+
* <p>This field may be {@code null} if the {@linkplain #JtaAdaptingDataSourceProvider() deprecated zero-argument
67+
* constructor of this class} is used.</p>
68+
*/
69+
private final Instance<Object> objects;
70+
71+
/**
72+
* The {@link TransactionManager} for the system.
73+
*
74+
* <p>This field may be {@code null} if the {@linkplain #JtaAdaptingDataSourceProvider() deprecated zero-argument
75+
* constructor of this class} is used.</p>
76+
*/
77+
private final TransactionManager transactionManager;
78+
79+
private final TransactionSynchronizationRegistry tsr;
80+
81+
private final boolean interposedSynchronizations;
82+
83+
private final boolean immediateEnlistment;
84+
85+
private final ExceptionConverter exceptionConverter;
86+
87+
88+
/*
89+
* Constructors.
90+
*/
91+
92+
93+
/**
94+
* Creates a new {@link JtaAdaptingDataSourceProvider}.
95+
*
96+
* @param objects an {@link Instance} providing access to CDI
97+
* beans; must not be {@code null}
98+
*
99+
* @param transactionManager a {@link TransactionManager}; must
100+
* not be {@code null}
101+
*
102+
* @param tsr a {@link TransactionSynchronizationRegistry}; must
103+
* not be {@code null}
104+
*
105+
* @exception NullPointerException if either {@code objects} or
106+
* {@code transactionManager} or {@code tsr} is {@code null}
107+
*/
108+
@Inject
109+
JtaAdaptingDataSourceProvider(Instance<Object> objects,
110+
TransactionManager transactionManager,
111+
TransactionSynchronizationRegistry tsr) {
112+
super();
113+
this.jtaDataSourcesByName = new ConcurrentHashMap<>();
114+
this.objects = Objects.requireNonNull(objects, "objects");
115+
this.transactionManager = Objects.requireNonNull(transactionManager, "transactionManager");
116+
this.tsr = Objects.requireNonNull(tsr, "tsr");
117+
this.interposedSynchronizations =
118+
Boolean.parseBoolean(System.getProperty("helidon.jta.interposedSynchronizations", "true"));
119+
this.immediateEnlistment =
120+
Boolean.parseBoolean(System.getProperty("helidon.jta.immediateEnlistment", "false"));
121+
Instance<ExceptionConverter> i = objects.select(ExceptionConverter.class);
122+
this.exceptionConverter = i.isUnsatisfied() ? null : i.get();
123+
}
124+
125+
126+
/*
127+
* Instance methods.
128+
*/
129+
130+
131+
/**
132+
* Supplies a {@link DataSource}.
133+
*
134+
* <p>This method may return {@code null}.</p>
135+
*
136+
* @param jta if {@code true}, the {@link DataSource} that is returned will be enrolled in JTA transactions
137+
*
138+
* @param useDefaultJta if {@code true}, and if the {@code jta} parameter value is {@code true}, the supplied {@code
139+
* dataSourceName} may be ignored and a default {@link DataSource} that will be enrolled in JTA transactions will be
140+
* returned if possible
141+
*
142+
* @param dataSourceName the name of the {@link DataSource} to return; may be {@code null}; ignored if both {@code
143+
* jta} and {@code useDefaultJta} are {@code true}
144+
*
145+
* @return an appropriate {@link DataSource}, or {@code null} in edge cases
146+
*
147+
* @see PersistenceUnitInfoBean#getJtaDataSource()
148+
*
149+
* @see PersistenceUnitInfoBean#getNonJtaDataSource()
150+
*/
151+
@Override // PersistenceUnitInfoBean.DataSourceProvider
152+
public DataSource getDataSource(boolean jta, boolean useDefaultJta, String dataSourceName) {
153+
if (jta) {
154+
if (dataSourceName == null) {
155+
return useDefaultJta ? this.getDefaultJtaDataSource() : null;
156+
}
157+
return this.getNamedJtaDataSource(dataSourceName);
158+
}
159+
return dataSourceName == null ? null : this.getNamedNonJtaDataSource(dataSourceName);
160+
}
161+
162+
private JtaAdaptingDataSource getDefaultJtaDataSource() {
163+
return
164+
jtaDataSourcesByName.computeIfAbsent(NULL_DATASOURCE_NAME,
165+
n -> new JtaAdaptingDataSource(this.transactionManager::getTransaction,
166+
this.tsr,
167+
this.interposedSynchronizations,
168+
this.exceptionConverter,
169+
this.objects.select(DataSource.class).get(),
170+
this.immediateEnlistment));
171+
}
172+
173+
private JtaAdaptingDataSource getNamedJtaDataSource(String name) {
174+
return
175+
jtaDataSourcesByName.computeIfAbsent(name,
176+
n -> new JtaAdaptingDataSource(this.transactionManager::getTransaction,
177+
this.tsr,
178+
this.interposedSynchronizations,
179+
this.exceptionConverter,
180+
this.objects.select(DataSource.class,
181+
NamedLiteral.of(n)).get(),
182+
this.immediateEnlistment));
183+
}
184+
185+
private DataSource getNamedNonJtaDataSource(String name) {
186+
return this.objects.select(DataSource.class, NamedLiteral.of(name)).get();
187+
}
188+
189+
@PreDestroy
190+
private void clear() {
191+
this.jtaDataSourcesByName.clear();
192+
}
193+
194+
}

0 commit comments

Comments
 (0)