• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

future-architect / uroborosql / #769

12 Sep 2024 02:26PM UTC coverage: 90.35% (-0.9%) from 91.21%
#769

Pull #332

HidekiSugimoto189
Refactoring log-related class and method names.
Pull Request #332: Enable per-SQL-ID log suppression (#322)

384 of 587 new or added lines in 32 files covered. (65.42%)

9 existing lines in 7 files now uncovered.

8885 of 9834 relevant lines covered (90.35%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.81
/src/main/java/jp/co/future/uroborosql/context/ExecutionContextProviderImpl.java
1
/**
2
 * Copyright (c) 2017-present, Future Corporation
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
package jp.co.future.uroborosql.context;
8

9
import java.io.IOException;
10
import java.lang.reflect.Modifier;
11
import java.net.JarURLConnection;
12
import java.net.URISyntaxException;
13
import java.net.URL;
14
import java.nio.file.Files;
15
import java.nio.file.Path;
16
import java.nio.file.Paths;
17
import java.sql.ResultSet;
18
import java.time.Clock;
19
import java.util.ArrayList;
20
import java.util.Collection;
21
import java.util.Collections;
22
import java.util.HashMap;
23
import java.util.HashSet;
24
import java.util.List;
25
import java.util.Map;
26
import java.util.Objects;
27
import java.util.Optional;
28
import java.util.Set;
29
import java.util.StringJoiner;
30
import java.util.concurrent.ConcurrentHashMap;
31
import java.util.jar.JarEntry;
32
import java.util.jar.JarFile;
33
import java.util.stream.Collectors;
34

35
import jp.co.future.uroborosql.config.SqlConfig;
36
import jp.co.future.uroborosql.event.AfterInitializeExecutionContextEvent;
37
import jp.co.future.uroborosql.log.support.ServiceLoggingSupport;
38
import jp.co.future.uroborosql.log.support.SettingLoggingSupport;
39
import jp.co.future.uroborosql.parameter.Parameter;
40
import jp.co.future.uroborosql.parameter.mapper.BindParameterMapper;
41
import jp.co.future.uroborosql.parameter.mapper.BindParameterMapperManager;
42
import jp.co.future.uroborosql.utils.CaseFormat;
43
import jp.co.future.uroborosql.utils.ObjectUtils;
44

45
/**
46
 * ExecutionContextプロバイダ実装
47
 *
48
 * @author H.Sugimoto
49
 */
50
public class ExecutionContextProviderImpl
1✔
51
                implements ExecutionContextProvider, ServiceLoggingSupport, SettingLoggingSupport {
52
        /** 定数パラメータプレフィックス */
53
        private String constParamPrefix = "CLS_";
1✔
54

55
        /** 定数クラス名(FQDN) */
56
        private List<String> constantClassNames = new ArrayList<>();
1✔
57
        /** Enum定数パッケージ名 */
58
        private List<String> enumConstantPackageNames = new ArrayList<>();
1✔
59

60
        /** 定数パラメータマップ */
61
        private Map<String, Parameter> constParameterMap = null;
1✔
62

63
        /** SQL設定クラス */
64
        private SqlConfig sqlConfig = null;
1✔
65

66
        /** ResultSetTypeの初期値 */
67
        private int defaultResultSetType = ResultSet.TYPE_FORWARD_ONLY;
1✔
68

69
        /** ResultSetConcurrencyの初期値*/
70
        private int defaultResultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
1✔
71

72
        /** パラメータ変換マネージャ */
73
        private BindParameterMapperManager parameterMapperManager = new BindParameterMapperManager(
1✔
74
                        Clock.systemDefaultZone());
1✔
75

76
        /**
77
         * {@inheritDoc}
78
         *
79
         * @see jp.co.future.uroborosql.context.ExecutionContextProvider#createExecutionContext()
80
         */
81
        @Override
82
        public ExecutionContext createExecutionContext() {
83
                var executionContext = new ExecutionContextImpl();
1✔
84
                executionContext.setSqlConfig(getSqlConfig());
1✔
85
                executionContext.setConstParameterMap(new ConcurrentHashMap<>(getConstParameterMap()));
1✔
86
                executionContext.setParameterMapperManager(
1✔
87
                                new BindParameterMapperManager(parameterMapperManager, getSqlConfig().getClock()));
1✔
88
                executionContext.setResultSetType(defaultResultSetType);
1✔
89
                executionContext.setResultSetConcurrency(defaultResultSetConcurrency);
1✔
90

91
                // ExecutionContext初期化後イベント発行
92
                if (getSqlConfig().getEventListenerHolder().hasAfterInitializeExecutionContextListener()) {
1✔
93
                        var eventObj = new AfterInitializeExecutionContextEvent(executionContext);
1✔
94
                        getSqlConfig().getEventListenerHolder().getAfterInitializeExecutionContextListeners()
1✔
95
                                        .forEach(listener -> listener.accept(eventObj));
1✔
96
                }
97
                return executionContext;
1✔
98
        }
99

100
        /**
101
         * {@inheritDoc}
102
         *
103
         * @see jp.co.future.uroborosql.config.SqlConfigAware#setSqlConfig(jp.co.future.uroborosql.config.SqlConfig)
104
         */
105
        @Override
106
        public void setSqlConfig(final SqlConfig sqlConfig) {
107
                this.sqlConfig = sqlConfig;
1✔
108
        }
1✔
109

110
        /**
111
         * {@inheritDoc}
112
         *
113
         * @see jp.co.future.uroborosql.config.SqlConfigAware#getSqlConfig()
114
         */
115
        @Override
116
        public SqlConfig getSqlConfig() {
117
                return sqlConfig;
1✔
118
        }
119

120
        /**
121
         * {@inheritDoc}
122
         *
123
         * @see jp.co.future.uroborosql.context.ExecutionContextProvider#initialize()
124
         */
125
        @Override
126
        public void initialize() {
127
                parameterMapperManager = new BindParameterMapperManager(parameterMapperManager, getSqlConfig().getClock());
1✔
128

129
                var paramMap = new HashMap<String, Parameter>(buildConstParamMap());
1✔
130
                paramMap.putAll(buildEnumConstParamMap());
1✔
131
                constParameterMap = Collections.unmodifiableMap(paramMap);
1✔
132
        }
1✔
133

134
        /**
135
         * 定数パラメータのMapを生成する
136
         *
137
         * @param paramMap 定数パラメータを保持するMap
138
         * @param targetClass 定数パラメータを生成する定数クラス。クラス内に内部クラスを持つ場合は内部クラスの定数フィールドもパラメータに登録する
139
         */
140
        protected void makeConstParamMap(final Map<String, Parameter> paramMap,
141
                        final Class<?> targetClass) {
142
                try {
143
                        var fieldPrefix = targetClass.isMemberClass() ? CaseFormat.UPPER_SNAKE_CASE
1✔
144
                                        .convert(targetClass.getSimpleName()) + "_" : "";
1✔
145
                        // 指定されたクラス直下の定数フィールドを追加
146
                        var fields = targetClass.getFields();
1✔
147
                        for (var field : fields) {
1✔
148
                                var mod = field.getModifiers();
1✔
149
                                if (Modifier.isFinal(mod) && Modifier.isStatic(mod)) {
1✔
150
                                        var value = field.get(null);
1✔
151
                                        if (parameterMapperManager.canAcceptByStandard(value)) {
1✔
152
                                                var fieldName = getConstParamPrefix() + fieldPrefix + field.getName();
1✔
153
                                                fieldName = fieldName.toUpperCase();
1✔
154
                                                var newValue = new Parameter(fieldName, field.get(null));
1✔
155
                                                var prevValue = paramMap.put(fieldName, newValue);
1✔
156
                                                if (prevValue != null) {
1✔
NEW
157
                                                        warnWith(SETTING_LOG)
×
NEW
158
                                                                        .setMessage("Duplicate constant name. Constant name:{}, old value:{} destroy.")
×
NEW
159
                                                                        .addArgument(fieldName)
×
NEW
160
                                                                        .addArgument(prevValue.getValue())
×
NEW
161
                                                                        .log();
×
162
                                                }
163
                                                infoWith(SETTING_LOG)
1✔
164
                                                                .setMessage("Constant [name:{}, value:{}] added to parameter.")
1✔
165
                                                                .addArgument(fieldName)
1✔
166
                                                                .addArgument(newValue.getValue())
1✔
167
                                                                .log();
1✔
168
                                        }
169
                                }
170
                        }
171

172
                        // 内部クラスを持つ場合
173
                        var memberClasses = targetClass.getDeclaredClasses();
1✔
174
                        for (var memberClass : memberClasses) {
1✔
175
                                var mod = memberClass.getModifiers();
1✔
176
                                if (Modifier.isFinal(mod) && Modifier.isPublic(mod)) {
1✔
177
                                        makeConstParamMap(paramMap, memberClass);
1✔
178
                                }
179
                        }
180
                } catch (IllegalArgumentException | IllegalAccessException | SecurityException ex) {
×
NEW
181
                        errorWith(LOG)
×
NEW
182
                                        .setMessage(ex.getMessage())
×
NEW
183
                                        .setCause(ex)
×
NEW
184
                                        .log();
×
185
                }
1✔
186
        }
1✔
187

188
        /**
189
         * Enum型の定数パラメータのMapを生成する
190
         *
191
         * @param paramMap 定数パラメータを保持するMap
192
         * @param packageName パッケージ名
193
         * @param targetClass 対象Enumクラス
194
         */
195
        protected void makeEnumConstParamMap(final Map<String, Parameter> paramMap,
196
                        final String packageName,
197
                        final Class<? extends Enum<?>> targetClass) {
198

199
                var fieldPrefix = CaseFormat.UPPER_SNAKE_CASE.convert(targetClass.getName().substring(
1✔
200
                                packageName.length() + 1)) + "_";
1✔
201

202
                var enumValues = targetClass.getEnumConstants();
1✔
203

204
                for (var value : enumValues) {
1✔
205
                        var fieldName = (getConstParamPrefix() + fieldPrefix + value.name().toUpperCase()).toUpperCase();
1✔
206
                        var newValue = new Parameter(fieldName, value);
1✔
207
                        var prevValue = paramMap.put(fieldName, newValue);
1✔
208
                        if (prevValue != null) {
1✔
NEW
209
                                warnWith(SETTING_LOG)
×
NEW
210
                                                .setMessage("Duplicate Enum name. Enum name:{}, old value:{} destroy.")
×
NEW
211
                                                .addArgument(fieldName)
×
NEW
212
                                                .addArgument(prevValue.getValue())
×
NEW
213
                                                .log();
×
214
                        }
215
                        infoWith(SETTING_LOG)
1✔
216
                                        .setMessage("Enum [name:{}, value:{}] added to parameter.")
1✔
217
                                        .addArgument(fieldName)
1✔
218
                                        .addArgument(newValue.getValue())
1✔
219
                                        .log();
1✔
220
                }
221
        }
1✔
222

223
        /**
224
         * {inheritDoc}
225
         *
226
         * @see ExecutionContextProvider#getConstParamPrefix()
227
         */
228
        @Override
229
        public String getConstParamPrefix() {
230
                return constParamPrefix;
1✔
231
        }
232

233
        /**
234
         * {inheritDoc}
235
         *
236
         * @see ExecutionContextProvider#setConstParamPrefix(String)
237
         */
238
        @Override
239
        public ExecutionContextProvider setConstParamPrefix(final String constParamPrefix) {
240
                this.constParamPrefix = constParamPrefix;
×
241
                return this;
×
242
        }
243

244
        /**
245
         * {inheritDoc}
246
         *
247
         * @see ExecutionContextProvider#getConstantClassNames()
248
         */
249
        @Override
250
        public List<String> getConstantClassNames() {
251
                return constantClassNames;
×
252
        }
253

254
        /**
255
         * {inheritDoc}
256
         *
257
         * @see ExecutionContextProvider#setConstantClassNames(List)
258
         */
259
        @Override
260
        public ExecutionContextProvider setConstantClassNames(final List<String> constantClassNames) {
261
                this.constantClassNames = constantClassNames;
1✔
262
                return this;
1✔
263
        }
264

265
        /**
266
         * {inheritDoc}
267
         *
268
         * @see ExecutionContextProvider#getConstParameterMap()
269
         */
270
        @Override
271
        public Map<String, Parameter> getConstParameterMap() {
272
                return constParameterMap;
1✔
273
        }
274

275
        /**
276
         * {inheritDoc}
277
         *
278
         * @see ExecutionContextProvider#getEnumConstantPackageNames()
279
         */
280
        @Override
281
        public List<String> getEnumConstantPackageNames() {
282
                return enumConstantPackageNames;
×
283
        }
284

285
        /**
286
         * {inheritDoc}
287
         *
288
         * @see ExecutionContextProvider#setEnumConstantPackageNames(List)
289
         */
290
        @Override
291
        public ExecutionContextProvider setEnumConstantPackageNames(final List<String> enumConstantPackageNames) {
292
                this.enumConstantPackageNames = enumConstantPackageNames;
1✔
293
                return this;
1✔
294
        }
295

296
        /**
297
         * {inheritDoc}
298
         *
299
         * @see ExecutionContextProvider#addBindParamMapper(BindParameterMapper)
300
         */
301
        @Override
302
        public ExecutionContextProvider addBindParamMapper(final BindParameterMapper<?> parameterMapper) {
303
                parameterMapperManager.addMapper(parameterMapper);
1✔
304
                return this;
1✔
305
        }
306

307
        /**
308
         * {inheritDoc}
309
         *
310
         * @see ExecutionContextProvider#removeBindParamMapper(BindParameterMapper)
311
         */
312
        @Override
313
        public ExecutionContextProvider removeBindParamMapper(final BindParameterMapper<?> parameterMapper) {
314
                parameterMapperManager.removeMapper(parameterMapper);
×
315
                return this;
×
316
        }
317

318
        /**
319
         * 定数クラスパラメータMap生成
320
         *
321
         * @return 定数クラスパラメータMap
322
         */
323
        private Map<? extends String, ? extends Parameter> buildConstParamMap() {
324
                var paramMap = new HashMap<String, Parameter>();
1✔
325
                for (var className : constantClassNames) {
1✔
326
                        if (ObjectUtils.isNotBlank(className)) {
1✔
327
                                try {
328
                                        var targetClass = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
1✔
329
                                        makeConstParamMap(paramMap, targetClass);
1✔
330
                                } catch (ClassNotFoundException ex) {
×
NEW
331
                                        errorWith(LOG)
×
NEW
332
                                                        .setMessage(ex.getMessage())
×
NEW
333
                                                        .setCause(ex)
×
NEW
334
                                                        .log();
×
335
                                }
1✔
336
                        }
337
                }
1✔
338
                return paramMap;
1✔
339
        }
340

341
        /**
342
         * Enum定数パラメータMap生成
343
         *
344
         * @return Enum定数パラメータMap
345
         */
346
        private Map<? extends String, ? extends Parameter> buildEnumConstParamMap() {
347
                var paramMap = new HashMap<String, Parameter>();
1✔
348
                for (var packageName : enumConstantPackageNames) {
1✔
349
                        if (ObjectUtils.isNotBlank(packageName)) {
1✔
350
                                for (var targetClass : listUpEnumClasses(packageName)) {
1✔
351
                                        makeEnumConstParamMap(paramMap, packageName, targetClass);
1✔
352
                                }
1✔
353
                        }
354
                }
1✔
355
                return paramMap;
1✔
356
        }
357

358
        /**
359
         * 対象パッケージ以下のクラスを取得
360
         *
361
         * @param packageName ルートパッケージ名
362
         * @return クラスリスト
363
         */
364
        @SuppressWarnings({ "unchecked", "rawtypes" })
365
        private Set<Class<? extends Enum<?>>> listUpEnumClasses(final String packageName) {
366
                var resourceName = packageName.replace('.', '/');
1✔
367
                var classLoader = Thread.currentThread().getContextClassLoader();
1✔
368
                List<URL> roots;
369
                try {
370
                        roots = Collections.list(classLoader.getResources(resourceName));
1✔
371
                } catch (IOException ex) {
×
NEW
372
                        errorWith(LOG)
×
NEW
373
                                        .setMessage(ex.getMessage())
×
NEW
374
                                        .setCause(ex)
×
NEW
375
                                        .log();
×
UNCOV
376
                        return Set.of();
×
377
                }
1✔
378

379
                var classes = new HashSet<Class<?>>();
1✔
380
                for (var root : roots) {
1✔
381
                        if ("file".equalsIgnoreCase(root.getProtocol())) {
1✔
382
                                try {
383
                                        classes.addAll(findEnumClassesWithFile(packageName, Paths.get(root.toURI())));
1✔
384
                                } catch (URISyntaxException ex) {
×
NEW
385
                                        errorWith(LOG)
×
NEW
386
                                                        .setMessage(ex.getMessage())
×
NEW
387
                                                        .setCause(ex)
×
NEW
388
                                                        .log();
×
389
                                }
1✔
390
                        }
391
                        if ("jar".equalsIgnoreCase(root.getProtocol())) {
1✔
392
                                try (var jarFile = ((JarURLConnection) root.openConnection()).getJarFile()) {
1✔
393
                                        classes.addAll(findEnumClassesWithJar(packageName, jarFile));
1✔
394
                                } catch (IOException ex) {
×
NEW
395
                                        errorWith(LOG)
×
NEW
396
                                                        .setMessage(ex.getMessage())
×
NEW
397
                                                        .setCause(ex)
×
NEW
398
                                                        .log();
×
399
                                }
1✔
400
                        }
401
                }
1✔
402

403
                return (Set) classes;
1✔
404
        }
405

406
        /**
407
         * classファイルから対象パッケージ以下のEnumクラスを取得
408
         *
409
         * @param packageName ルートパッケージ名
410
         * @param dir 対象ディレクトリ
411
         * @return クラスリスト
412
         * @throws ClassNotFoundException エラー
413
         * @throws IOException
414
         */
415
        private Set<Class<?>> findEnumClassesWithFile(final String packageName, final Path dir) {
416
                var prefix = packageName + ".";
1✔
417
                try (var stream = Files.walk(dir)) {
1✔
418
                        return stream.filter(entry -> entry.getFileName().toString().endsWith(".class"))
1✔
419
                                        .flatMap(file -> {
1✔
420
                                                var joiner = new StringJoiner(".", prefix, "");
1✔
421
                                                dir.relativize(file).forEach(p -> joiner.add(p.toString()));
1✔
422
                                                var className = joiner.toString().replaceAll(".class$", "");
1✔
423
                                                return loadEnum(className).stream();
1✔
424
                                        })
425
                                        .filter(Objects::nonNull)
1✔
426
                                        .collect(Collectors.toSet());
1✔
427
                } catch (IOException ex) {
×
NEW
428
                        errorWith(LOG)
×
NEW
429
                                        .setMessage(ex.getMessage())
×
NEW
430
                                        .setCause(ex)
×
NEW
431
                                        .log();
×
UNCOV
432
                        return Set.of();
×
433
                }
434
        }
435

436
        /**
437
         * jarファイルから対象パッケージ以下のEnumクラスを取得
438
         *
439
         * @param packageName ルートパッケージ名
440
         * @param jarFile jarファイル
441
         * @return クラスリスト
442
         * @throws ClassNotFoundException エラー
443
         * @throws IOException
444
         */
445
        private Collection<? extends Class<?>> findEnumClassesWithJar(final String packageName,
446
                        final JarFile jarFile) {
447
                var resourceName = packageName.replace('.', '/');
1✔
448
                return Collections.list(jarFile.entries()).stream()
1✔
449
                                .map(JarEntry::getName)
1✔
450
                                .filter(name -> name.startsWith(resourceName) && name.endsWith(".class"))
1✔
451
                                .map(name -> name.replace('/', '.').replaceAll(".class$", ""))
1✔
452
                                .flatMap(className -> loadEnum(className).stream())
1✔
453
                                .filter(Objects::nonNull)
1✔
454
                                .collect(Collectors.toSet());
1✔
455
        }
456

457
        /**
458
         * Enumクラスをロード<br>
459
         * 指定クラスがEnumでない場合はemptyを返す
460
         *
461
         * @param className クラス名
462
         * @return ロードしたEnumクラス
463
         */
464
        private Optional<Class<?>> loadEnum(final String className) {
465
                try {
466
                        var type = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
1✔
467
                        if (type.isEnum()) {
1✔
468
                                return Optional.of(type);
1✔
469
                        }
470
                } catch (ClassNotFoundException ex) {
×
NEW
471
                        errorWith(LOG)
×
NEW
472
                                        .setMessage(ex.getMessage())
×
NEW
473
                                        .setCause(ex)
×
NEW
474
                                        .log();
×
475
                }
1✔
476
                return Optional.empty();
1✔
477
        }
478

479
        /**
480
         * {@inheritDoc}
481
         *
482
         * @see jp.co.future.uroborosql.context.ExecutionContextProvider#setDefaultResultSetType(int)
483
         */
484
        @Override
485
        public ExecutionContextProvider setDefaultResultSetType(final int resultSetType) {
486
                defaultResultSetType = resultSetType;
1✔
487
                return this;
1✔
488
        }
489

490
        /**
491
         * {@inheritDoc}
492
         *
493
         * @see jp.co.future.uroborosql.context.ExecutionContextProvider#setDefaultResultSetConcurrency(int)
494
         */
495
        @Override
496
        public ExecutionContextProvider setDefaultResultSetConcurrency(final int resultSetConcurrency) {
497
                defaultResultSetConcurrency = resultSetConcurrency;
1✔
498
                return this;
1✔
499
        }
500

501
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc