EMMA Coverage Report (generated Tue Apr 17 08:51:20 BST 2007)
[all classes][org.jtoolkit.essence.app.impl]

COVERAGE SUMMARY FOR SOURCE FILE [ContainerImpl.java]

nameclass, %method, %block, %line, %
ContainerImpl.java100% (4/4)97%  (29/30)78%  (1279/1633)84%  (233/278)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ContainerImpl$InvokeMethod100% (1/1)100% (2/2)49%  (23/47)67%  (8/12)
call (): Void 100% (1/1)31%  (11/35)43%  (3/7)
ContainerImpl$InvokeMethod (String, Object, Method): void 100% (1/1)100% (12/12)100% (5/5)
     
class ContainerImpl$ComponentStatus100% (1/1)100% (3/3)73%  (295/406)81%  (51.9/64)
attemptResolution (Map): boolean 100% (1/1)70%  (243/348)78%  (41.3/53)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
ContainerImpl$ComponentStatus (ContainerImpl, String): void 100% (1/1)92%  (46/50)99%  (9.9/10)
     
class ContainerImpl100% (1/1)96%  (22/23)81%  (932/1151)86%  (171/200)
access$500 (): Log 0%   (0/1)0%   (0/2)0%   (0/1)
checkLP (Object, String, String): Object 100% (1/1)65%  (33/51)88%  (7/8)
close (): void 100% (1/1)66%  (104/158)74%  (22.2/30)
acquireExecutor (String): ScheduledExecutorService 100% (1/1)68%  (56/82)73%  (9.5/13)
ContainerImpl (String, String): void 100% (1/1)69%  (108/156)74%  (20.7/28)
call (String, boolean): int 100% (1/1)82%  (172/210)87%  (37.6/43)
<static initializer> 100% (1/1)86%  (12/14)95%  (2.8/3)
getComponent (String, Class): Object 100% (1/1)89%  (77/87)83%  (12.5/15)
initComponents (): void 100% (1/1)92%  (258/279)97%  (40.7/42)
access$000 (ContainerImpl): ComponentModel 100% (1/1)100% (3/3)100% (1/1)
access$100 (ContainerImpl): ComponentBuilder [] 100% (1/1)100% (3/3)100% (1/1)
access$200 (ContainerImpl): Map 100% (1/1)100% (3/3)100% (1/1)
access$300 (ContainerImpl): Named$Source 100% (1/1)100% (3/3)100% (1/1)
access$400 (ContainerImpl, String): ScheduledExecutorService 100% (1/1)100% (4/4)100% (1/1)
getName (): String 100% (1/1)100% (3/3)100% (1/1)
getNames (): Set 100% (1/1)100% (6/6)100% (2/2)
getProperties (): Named$Source 100% (1/1)100% (3/3)100% (1/1)
getProperties (String): Named$Source 100% (1/1)100% (5/5)100% (1/1)
getUrl (): String 100% (1/1)100% (3/3)100% (1/1)
getValue (String): Object 100% (1/1)100% (5/5)100% (1/1)
isClosed (): boolean 100% (1/1)100% (3/3)100% (1/1)
remainingConfig (): Set 100% (1/1)100% (4/4)100% (1/1)
removeConfigAs (String, Class): Object [] 100% (1/1)100% (64/64)100% (9/9)
     
class ContainerImpl$ComponentStatus$1100% (1/1)100% (2/2)100% (29/29)100% (2/2)
ContainerImpl$ComponentStatus$1 (ContainerImpl$ComponentStatus, Map, Map): void 100% (1/1)100% (12/12)100% (1/1)
call (): Object 100% (1/1)100% (17/17)100% (1/1)

1package org.jtoolkit.essence.app.impl;
2 
3import org.apache.commons.logging.Log;
4import org.apache.commons.logging.LogFactory;
5import org.jetbrains.annotations.NotNull;
6import org.jetbrains.annotations.Nullable;
7import org.jtoolkit.essence.app.ComponentBuilder;
8import org.jtoolkit.essence.app.Container;
9import org.jtoolkit.essence.app.Main;
10import static org.jtoolkit.essence.app.Main.registerComponent;
11import org.jtoolkit.essence.app.Runs;
12import org.jtoolkit.essence.app.net.NetObject;
13import org.jtoolkit.essence.app.net.ServerDataSocket;
14import org.jtoolkit.essence.app.pojo.impl.ComponentHelper;
15import org.jtoolkit.essence.app.pojo.impl.DataValueClass;
16import org.jtoolkit.essence.concurrency.Immutable;
17import org.jtoolkit.essence.concurrency.ThreadSafe;
18import org.jtoolkit.essence.concurrency.Threads;
19import org.jtoolkit.essence.utils.Closeable;
20import org.jtoolkit.essence.utils.Factory;
21import org.jtoolkit.essence.utils.IOUtils;
22import org.jtoolkit.essence.utils.Named;
23import static org.jtoolkit.essence.utils.StringUtils.expand;
24import static org.jtoolkit.essence.utils.StringUtils.isSet;
25import org.jtoolkit.essence.utils.impl.MapArray;
26import static org.jtoolkit.essence.utils.impl.MapArray.asKeys;
27 
28import javax.management.InstanceNotFoundException;
29import javax.management.MBeanServer;
30import javax.management.MalformedObjectNameException;
31import javax.management.ObjectName;
32import java.io.IOException;
33import static java.lang.management.ManagementFactory.getPlatformMBeanServer;
34import java.lang.reflect.Array;
35import java.lang.reflect.InvocationTargetException;
36import java.lang.reflect.Method;
37import java.lang.reflect.Modifier;
38import java.util.*;
39import java.util.concurrent.*;
40 
41/*
42   Copyright 2006 Peter Lawrey
43 
44   Licensed under the Apache License, Version 2.0 (the "License");
45   you may not use this file except in compliance with the License.
46   You may obtain a copy of the License at
47 
48       http://www.apache.org/licenses/LICENSE-2.0
49 
50   Unless required by applicable law or agreed to in writing, software
51   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
52   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
53   See the License for the specific language governing permissions and
54   limitations under the License.
55*/
56 
57/**
58 * @author Peter Lawrey
59 */
60@SuppressWarnings({"unchecked", "OverlyCoupledClass"})
61public class ContainerImpl implements Container {
62    private static final Log LOG = LogFactory.getLog(ContainerImpl.class);
63    private static final int RESOLVE_LOOPS = 7;
64    private static final ScheduledExecutorService[] NO_SES = new ScheduledExecutorService[0];
65    private static final String THREAD_POOL_SIZE = "thread.pool.size";
66 
67    private final String name;
68    private final String url;
69    private final ComponentModel componentModel;
70    private final Map<String, List<String[]>> containerConfig;
71    private final Map<String, Object> components = new LinkedHashMap<String, Object>();
72    private final Map<String, String> componentsBoundToLP = new LinkedHashMap<String, String>();
73    private final Map<String, ScheduledExecutorService> executors = new LinkedHashMap<String, ScheduledExecutorService>();
74    private final Named.Source<String> properties;
75    private final Set<ObjectName> registered = new LinkedHashSet<ObjectName>();
76 
77    @Nullable private ServerDataSocket sds = null;
78    @Nullable private Factory dsf = null;
79 
80    @Nullable private ComponentBuilder[] componentBuilders = null;
81    private boolean hasInit = false;
82    private boolean closed = false;
83    private static final String JMX_COMPONENT_GROUP = ",Component=";
84    private static final String JMX_BUILDER_GROUP = ":Builder=";
85 
86    public ContainerImpl(@NotNull String name, @NotNull String url) throws InstantiationException, IOException {
87        this.name = name;
88        this.url = url;
89        containerConfig = IOUtils.readTabs(IOUtils.getInputStream(url));
90        componentModel = new ComponentModel(name, containerConfig);
91        Map<String, String> propertiesMap = componentModel.getContainerProperties();
92        String serverHostname = propertiesMap.get(SERVER_HOSTNAME);
93        if (serverHostname == null) propertiesMap.put(SERVER_HOSTNAME, serverHostname = Main.SERVER_NAME);
94 
95        properties = new ContextSource<String>(name, new MapSource<String>(propertiesMap), Main.generateContexts(name + '.' + serverHostname));
96 
97        // set system proeprties using the "System" component.
98        Named.Source<String> systemProperties = componentModel.getComponentProperties("System");
99        for (String key : systemProperties.getNames()) {
100            String value = systemProperties.getValue(key);
101            String prev = System.getProperty(key);
102            if (prev != null && !prev.equals(value))
103                LOG.info(name + ": System property " + key + " left as '" + prev + "' not changed to '" + value + '\'');
104            else if (prev == null)
105                System.setProperty(key, value);
106        }
107    }
108 
109    @NotNull private ScheduledExecutorService acquireExecutor(@NotNull String threadName) {
110        synchronized (executors) {
111            ScheduledExecutorService ses = executors.get(threadName);
112            if (ses == null) {
113                Named.Source<String> threadProperties = getProperties(threadName);
114                int threadPoolSize = 1;
115                String threadPoolSizeStr = threadProperties.getValue(THREAD_POOL_SIZE);
116                if (threadPoolSizeStr != null)
117                    threadPoolSize = Integer.parseInt(threadPoolSizeStr);
118                if (threadPoolSize != 1 && LOG.isInfoEnabled())
119                    LOG.info(getName() + ": Logical process started with " + threadPoolSize + " threads");
120                executors.put(threadName, ses = Threads.createMultiSES(Threads.LOGICAL_PROCESS + name + '$' + threadName, threadPoolSize, Thread.NORM_PRIORITY));
121            }
122            return ses;
123        }
124    }
125 
126    public int call(@NotNull String method, boolean inEachThread) {
127        initComponents();
128        int count = 0;
129        for (String componentName : components.keySet()) {
130            Object obj = components.get(componentName);
131            // is the component in a component builder?
132            if (obj == null) {
133                assert componentBuilders != null;
134                for (ComponentBuilder cb : componentBuilders) {
135                    if (cb.getName().equals(componentName)) {
136                        obj = cb;
137                        break;
138                    }
139                    obj = cb.resolveComponent(componentName, null);
140                    if (obj != null) break;
141                }
142                if (obj == null) throw new IllegalStateException("Unable to find component " + componentName);
143            }
144            try {
145                Method m = obj.getClass().getMethod(method);
146                if ((m.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC) {
147                    Callable<Void> runnable = new InvokeMethod(componentName, obj, m);
148                    ComponentModel.ComponentData componentData = componentModel.getComponentData(componentName);
149                    if (inEachThread && componentData != null) {
150                        String lp = componentData.logicalProcess;
151                        ScheduledExecutorService ses = acquireExecutor(lp);
152                        ses.submit(runnable);
153                    } else {
154                        try {
155                            runnable.call();
156                        } catch (Exception e) {
157                            throw new RuntimeException(e);
158                        }
159                    }
160                    count++;
161                }
162            } catch (NoSuchMethodException ignored) {
163                // ignored.
164            }
165        }
166 
167        if (Runs.START.equals(method)) {
168            String serverPortStr = properties.getValue(SERVER_PORT);
169            if (sds == null && isSet(serverPortStr)) {
170                // noinspection CallToThreadYield
171                Thread.yield(); // give start a chance.
172                int serverPort = Integer.parseInt(serverPortStr);
173                try {
174                    sds = new ServerDataSocket(name, serverPort, dsf = new NetObject.ServerProcessorFactory(this));
175                    String dsName = name + ":Name=server-socket";
176                    Main.registerComponent(dsName, sds, "Server socket port " + serverPortStr);
177                } catch (IOException e) {
178                    throw new IllegalStateException("Unable to start server port " + serverPortStr, e);
179                }
180                // noinspection CallToThreadYield
181                Thread.yield(); // give the server a chance to start.
182            }
183        }
184 
185        return count;
186    }
187 
188    public void close() {
189        if (!closed) {
190            call("stopping", true);
191            // noinspection CallToThreadYield
192            Thread.yield();
193        }
194        IOUtils.close(dsf);
195        IOUtils.close(sds);
196        call("stop", false);
197        call("close", false);
198        MBeanServer bs = getPlatformMBeanServer();
199        Set<ObjectName> instances;
200        try {
201            instances = bs.queryNames(new ObjectName(name + ":*"), null);
202            instances.addAll(registered);
203        } catch (MalformedObjectNameException e) {
204            LOG.error(getName() + ": Unexpected error", e);
205            instances = registered;
206        }
207        for (ObjectName oname : instances) {
208            try {
209                Object obj = bs.getObjectInstance(oname);
210                if (obj instanceof Closeable) {
211                    Closeable closeable = (Closeable) obj;
212                    if (!closeable.isClosed()) {
213                        if (LOG.isInfoEnabled()) LOG.info(getName() + ": Closing resource " + closeable);
214                        IOUtils.close(closeable);
215                    }
216                }
217            } catch (InstanceNotFoundException e) {
218                if (LOG.isDebugEnabled()) LOG.debug(getName() + ": Unable to get for closing MBean object for " + oname, e);
219            }
220            Main.unregisterComponent(oname);
221        }
222 
223        closed = true;
224        ScheduledExecutorService[] sesArr = executors.values().toArray(NO_SES);
225        for (ScheduledExecutorService ses : sesArr) {
226            ses.shutdownNow();
227        }
228    }
229 
230    @Nullable public Object getValue(@NotNull String componentName) {
231        return getComponent(componentName, null);
232    }
233 
234    @Nullable public <T> T getComponent(@NotNull String componentName, @Nullable Class<T> type) {
235        initComponents();
236        T obj = (T) components.get(componentName);
237        if (obj != null)
238            return checkLP(obj, componentName, EXTERNAL);
239        assert componentBuilders != null;
240        for (ComponentBuilder cb : componentBuilders) {
241            obj = cb.resolveComponent(componentName, type);
242            if (obj != null) {
243                Object obj2 = cb.resolveComponent(componentName, null);
244                if (obj2 == null)
245                    obj2 = obj;
246                String jmxName = getName() + JMX_BUILDER_GROUP + cb.getName() + JMX_COMPONENT_GROUP + componentName;
247                registerComponent(jmxName, obj2, componentName);
248                return checkLP(obj, componentName, EXTERNAL);
249            }
250        }
251        return null;
252    }
253 
254    private <T> T checkLP(T obj, String componentName, String logicalProcess) {
255        if (obj.getClass().getAnnotation(ThreadSafe.class) != null || obj.getClass().getAnnotation(Immutable.class) != null)
256            return obj;
257 
258        String lp = componentsBoundToLP.get(componentName);
259        if (lp == null) {
260            componentsBoundToLP.put(componentName, logicalProcess);
261        } else if (!lp.equals(logicalProcess)) {
262            throw new IllegalStateException(componentName + ": Is bound to, or was used by " + lp + " cannot be accessed by " + logicalProcess);
263        }
264        return obj;
265    }
266 
267    @NotNull public Set<String> getNames() {
268        initComponents();
269        return components.keySet();
270    }
271 
272    @NotNull public String getName() {
273        return name;
274    }
275 
276    @NotNull public Source<String> getProperties() {
277        return properties;
278    }
279 
280    @NotNull public Named.Source<String> getProperties(@NotNull String componentName) {
281        return componentModel.getComponentProperties(componentName);
282    }
283 
284    @NotNull public String getUrl() {
285        return url;
286    }
287 
288    public boolean isClosed() {
289        return closed;
290    }
291 
292    @NotNull public Set<String> remainingConfig() {
293        return containerConfig.keySet();
294    }
295 
296    @NotNull
297    public <T> T[] removeConfigAs(@NotNull String configName, @NotNull Class<T> clazz) throws InstantiationException {
298        List<T> ret = new ArrayList<T>();
299        List<String[]> strings = containerConfig.remove(configName);
300        if (strings != null && strings.size() > 1) {
301            String[] heading = strings.get(0);
302            Map<String, Integer> keys = asKeys(heading);
303            DataValueClass<T> ph = DataValueClass.acquire(clazz);
304            for (String[] row : strings.subList(1, strings.size())) {
305                ret.add(ph.build(new MapArray(keys, row), componentModel.pojoContext));
306            }
307        }
308        return ret.toArray((T[]) Array.newInstance(clazz, ret.size()));
309    }
310 
311    private void initComponents() {
312        if (hasInit) return;
313        hasInit = true;
314        //// Build the Component Builders.
315        Map<String, ComponentModel.ComponentBuilderData> builders = componentModel.getContainerBuilders();
316        componentBuilders = new ComponentBuilder[builders.size()];
317        int count = 0;
318        for (String builderName : builders.keySet()) {
319            ComponentModel.ComponentBuilderData componentBuilderData = builders.get(builderName);
320            Class clazz = componentBuilderData.componentBuilder;
321            ComponentHelper componentHelper = ComponentHelper.acquire(clazz);
322            Named.Source<String> properties = componentModel.getComponentProperties(builderName);
323            Map<String, Object> defaults = new LinkedHashMap<String, Object>();
324            defaults.put(DEFAULT_CONTAINER_NAME, getName());
325            defaults.put(DEFAULT_NAME, builderName);
326            defaults.put(DEFAULT_CONTAINER, this);
327            defaults.put(DEFAULT_PURPOSE, componentBuilderData.purpose);
328            ComponentBuilder componentBuilder = (ComponentBuilder) componentHelper.build(properties, defaults, componentModel.pojoContext);
329            assert componentBuilders != null;
330            componentBuilders[count++] = componentBuilder;
331            components.put(componentBuilder.getName(), null);
332            registerComponent(getName() + JMX_BUILDER_GROUP + builderName, componentBuilder, componentBuilderData.purpose);
333        }
334        //// Build the remaining components.
335        Map<String, ComponentStatus> unresolved = new LinkedHashMap<String, ComponentStatus>();
336        for (String componentName : componentModel.getComponentNames()) {
337            unresolved.put(componentName, new ComponentStatus(componentName));
338        }
339        Map<String, ComponentStatus> resolved = new LinkedHashMap<String, ComponentStatus>();
340        for (int i = 0; i < RESOLVE_LOOPS && !unresolved.isEmpty(); i++) {
341            for (String unresolvedName : unresolved.keySet().toArray(new String[unresolved.size()])) {
342                ComponentStatus cs = unresolved.get(unresolvedName);
343                assert cs != null;
344                if (!cs.attemptResolution(resolved)) continue;
345                unresolved.remove(unresolvedName);
346                resolved.put(unresolvedName, cs);
347                Object component = cs.component;
348                String description = cs.componentData.purpose;
349                if (component != null) {
350                    components.put(unresolvedName, component);
351                    componentsBoundToLP.put(cs.componentName, cs.componentData.logicalProcess);
352                    String jmxName = getName() + ":LogicalProcess=" + Threads.LOGICAL_PROCESS + cs.componentData.logicalProcess + JMX_COMPONENT_GROUP + unresolvedName;
353                    registerComponent(jmxName, component, description);
354                }
355            }
356        }
357        if (!unresolved.isEmpty())
358            throw new IllegalArgumentException("Unable to resolve dependancies for " + unresolved.keySet());
359    }
360 
361    class ComponentStatus {
362        final String componentName;
363        final ComponentModel.ComponentData componentData;
364        final ComponentHelper componentHelper;
365        final Named.Source<String> componentReferences;
366        final Map<String, Class> referenceTypes;
367        final Named.Source<String> componentProperties;
368        Object component = null;
369 
370        ComponentStatus(String componentName) {
371            this.componentName = componentName;
372            componentProperties = componentModel.getComponentProperties(componentName);
373            componentReferences = componentModel.getComponentReferences(componentName);
374            componentData = componentModel.getComponentData(componentName);
375            assert componentData != null;
376            //noinspection unchecked
377            componentHelper = ComponentHelper.acquire(componentData.className);
378            referenceTypes = componentHelper.getReferenceTypes();
379/*
380            for (String referenceTo : componentReferences.values()) {
381                if (componentModel.getComponentData(referenceTo) == null)
382                    throw new IllegalArgumentException("Component " + componentName + " refers to missing component " + referenceTo);
383            }
384*/
385        }
386 
387        public boolean attemptResolution(Map<String, ComponentStatus> resolved) {
388            final Map<String, Object> values = new LinkedHashMap<String, Object>();
389            String logicalProcess = componentData.logicalProcess;
390 
391            LOOP:
392            for (String referenceName : componentReferences.getNames()) {
393                String referenceTo = componentReferences.getValue(referenceName);
394                if (referenceTo == null) {
395                    values.put(referenceName, null);
396                    continue;
397                }
398                Class type = referenceTypes.get(referenceName);
399                if (type == null)
400                    throw new IllegalArgumentException(COMPONENT + componentName +
401                            " has a reference " + referenceName + " but unable to determine its type in " + componentData.className);
402                // see if a component builder can resolve the reference.
403                assert componentBuilders != null;
404                for (ComponentBuilder cb : componentBuilders) {
405                    // is there a version which is not locked to a logical process.?
406                    Object obj = cb.resolveComponent(referenceTo, type);
407                    String name;
408                    if (obj != null) {
409                        name = "Builder=" + cb.getName() + JMX_COMPONENT_GROUP + referenceTo;
410                    } else {
411                        obj = cb.resolveComponent(referenceTo, type);
412                        name = "LogicalProcess=" + logicalProcess + ",Builder=" + cb.getName() + JMX_COMPONENT_GROUP + referenceTo;
413                    }
414                    if (obj != null) {
415                        // just add the name. The object cannot be added as its type vary the object obtained.
416                        components.put(referenceTo, null);
417                        values.put(referenceName, obj);
418                        registerComponent(getName() + ':' + name, obj, referenceTo);
419                        continue LOOP;
420                    }
421                }
422 
423                ComponentStatus cs = resolved.get(referenceTo);
424                if (cs == null)
425                    return false;
426                Object component = cs.component;
427                values.put(referenceName, component);
428            }
429            for (String property : componentProperties.getNames()) {
430                String value = componentProperties.getValue(property);
431                assert value != null;
432                values.put(property, expand(value, properties));
433            }
434            final Map<String, Object> defaults = new LinkedHashMap<String, Object>();
435            defaults.put(DEFAULT_CONTAINER_NAME, getName());
436            defaults.put(DEFAULT_NAME, componentName);
437            defaults.put(DEFAULT_PURPOSE, componentData.purpose);
438            ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) acquireExecutor(logicalProcess);
439            defaults.put(DEFAULT_EXECUTOR, executor);
440            Future<Object> obj = executor.submit(new Callable<Object>() {
441                public Object call() throws Exception {
442                    return componentHelper.build(new MapSource<Object>(values), defaults, componentModel.pojoContext);
443                }
444            });
445            try {
446                component = obj.get();
447                int poolSize = executor.getMaximumPoolSize();
448                if (poolSize > 1 && Threads.isNotThreadSafe(component))
449                    throw new IllegalArgumentException(getName() + ": Attempt to use non @ThreadSafe component " + componentName + " in logical process " + logicalProcess + " which has a pool size of " + poolSize);
450            } catch (InterruptedException e) {
451                throw new IllegalStateException("Interrupted creating " + componentName, e);
452            } catch (ExecutionException e) {
453                Throwable cause = e.getCause();
454                if (cause instanceof RuntimeException)
455                    throw (RuntimeException) cause;
456                throw new IllegalStateException("Failed to create " + componentName, e);
457            }
458            return true;
459        }
460    }
461 
462    private static class InvokeMethod implements Callable<Void> {
463        private final String componentName;
464        private final Object object;
465        private final Method method;
466 
467        InvokeMethod(String componentName, Object object, Method method) {
468            this.componentName = componentName;
469            this.object = object;
470            this.method = method;
471        }
472 
473        @Nullable public Void call() {
474            try {
475                method.invoke(object);
476            } catch (IllegalAccessException ignored) {
477                throw new InternalError(ignored.toString());
478            } catch (InvocationTargetException e) {
479                LOG.error(componentName + ": Failed to call " + method, e.getCause());
480            }
481            return null;
482        }
483    }
484}

[all classes][org.jtoolkit.essence.app.impl]
EMMA 2.0.5312 (C) Vladimir Roubtsov