Test Failed
Pull Request — master (#190)
by Vincent
16:13
created

loadEngine()   A

Complexity

Conditions 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 7
dl 0
loc 10
ccs 1
cts 1
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2021 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.game.admin.script;
21
22
import fr.quatrevieux.araknemu.core.di.Container;
23
import fr.quatrevieux.araknemu.game.admin.Command;
24
import fr.quatrevieux.araknemu.game.admin.context.AbstractContextConfigurator;
25
import fr.quatrevieux.araknemu.game.admin.context.Context;
26
import groovy.util.GroovyScriptEngine;
27
import org.apache.logging.log4j.Logger;
28
29
import java.io.IOException;
30
import java.lang.reflect.Constructor;
31
import java.lang.reflect.InvocationTargetException;
32
import java.net.MalformedURLException;
33
import java.net.URL;
34
import java.nio.file.Files;
35
import java.nio.file.Path;
36
import java.util.concurrent.atomic.AtomicReference;
37
import java.util.function.Function;
38
import java.util.stream.Stream;
39
40
/**
41
 * Load commands from groovy scripts
42
 *
43
 * @param <C> The context type
44
 */
45
public final class ScriptLoaderContextConfigurator<C extends Context> extends AbstractContextConfigurator<C> {
46
    private final Path path;
47
    private final Function<C, Container> containerResolver;
48
    private final Logger logger;
49
50
    private final AtomicReference<GroovyScriptEngine> engine = new AtomicReference<>();
51 1
52 1
    public ScriptLoaderContextConfigurator(Path path, Function<C, Container> containerResolver, Logger logger) {
53 1
        this.path = path;
54 1
        this.containerResolver = containerResolver;
55
        this.logger = logger;
56
    }
57 1
58
    @Override
59
    public void configure(C context) {
60
        if (!Files.isDirectory(path)) {
61 1
            return;
62 1
        }
63
64
        // Issue #185 : engine must be loaded only if directory exists
65
        loadEngine();
66 1
67 1
        final Container container = containerResolver.apply(context);
68
69
        try (Stream<Path> stream = Files.list(path)) {
70 1
            stream.forEach(file -> {
71
                logger.debug("Load command script {}", file.toAbsolutePath().toString());
72 1
73 1
                try {
74 1
                    final Class type = engine.get().loadScriptByName(file.getFileName().toString());
75
76
                    if (Command.class.isAssignableFrom(type)) {
77 1
                        logger.debug("Find command {}", type.getSimpleName());
78
                        add(instantiate(container, type));
79 1
                    }
80 1
                } catch (Exception e) {
81 1
                    logger.error("Fail to load command script", e);
82
                }
83 1
            });
84 1
        } catch (IOException e) {
85 1
            logger.error("Fail to open commands scripts directory", e);
86 1
        }
87
    }
88
89 1
    private Command instantiate(Container container, Class<? extends Command> type) throws InstantiationException, IllegalAccessException, InvocationTargetException {
90 1
        for (Constructor constructor : type.getConstructors()) {
91
            final Object[] parameters = resolveArguments(container, constructor);
92
93 1
            if (parameters != null) {
94 1
                return (Command) constructor.newInstance(parameters);
95
            }
96 1
        }
97 1
98
        // No constructor found : try to instantiate without constructor
99
        return type.newInstance();
100
    }
101
102
    private Object[] resolveArguments(Container container, Constructor constructor) {
103
        final Object[] parameters = new Object[constructor.getParameterCount()];
104
        final Class[] parametersTypes = constructor.getParameterTypes();
105
106 1
        for (int i = 0; i < parameters.length; ++i) {
107 1
            if (!container.has(parametersTypes[i])) {
108
                return null;
0 ignored issues
show
Code Smell introduced by
When the calling method is expecting an Array it is (null-)safer to return an empty array instead of null.
Loading history...
109 1
            }
110 1
111
            parameters[i] = container.get(parametersTypes[i]);
112
        }
113
114 1
        return parameters;
115
    }
116
117 1
    private synchronized void loadEngine() {
118
        if (engine.get() != null) {
119
            return;
120
        }
121
122
        try {
123
            engine.set(new GroovyScriptEngine(new URL[] {path.toUri().toURL()}));
124
        } catch (MalformedURLException e) {
125
            // Should not occurs
126
            throw new RuntimeException();
0 ignored issues
show
Best Practice introduced by
Dedicated exceptions should be preferred over throwing the generic Exception.
Loading history...
127
        }
128
    }
129
}
130