public synchronized void start()

in tomee/tomee-embedded/src/main/java/org/apache/tomee/embedded/TomEEEmbeddedApplicationRunner.java [132:336]


    public synchronized void start(final Class<?> marker, final Properties config, final String... args) throws Exception {
        if (started) {
            return;
        }

        ensureAppInit(marker);
        started = true;

        final Class<?> appClass = app.getClass();
        final AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(ancestors(appClass)));

        // setup the container config reading class annotation, using a randome http port and deploying the classpath
        final Configuration configuration = new Configuration();
        final ContainerProperties props = appClass.getAnnotation(ContainerProperties.class);
        if (props != null) {
            final Properties runnerProperties = new Properties();
            for (final ContainerProperties.Property p : props.value()) {
                final String name = p.name();
                if (name.startsWith("tomee.embedded.application.runner.")) { // allow to tune the Configuration
                    // no need to filter there since it is done in loadFromProperties()
                    runnerProperties.setProperty(name.substring("tomee.embedded.application.runner.".length()), p.value());
                } else {
                    configuration.property(name, StrSubstitutor.replaceSystemProperties(p.value()));
                }
            }
            if (!runnerProperties.isEmpty()) {
                configuration.loadFromProperties(runnerProperties);
            }
        }
        configuration.loadFromProperties(System.getProperties()); // overrides, note that some config are additive by design

        final List<Method> annotatedMethods = finder.findAnnotatedMethods(org.apache.openejb.testing.Configuration.class);
        if (annotatedMethods.size() > 1) {
            throw new IllegalArgumentException("Only one @Configuration is supported: " + annotatedMethods);
        }
        for (final Method m : annotatedMethods) {
            final Object o = m.invoke(app);
            if (Properties.class.isInstance(o)) {
                final Properties properties = Properties.class.cast(o);
                if (configuration.getProperties() == null) {
                    configuration.setProperties(new Properties());
                }
                configuration.getProperties().putAll(properties);
            } else {
                throw new IllegalArgumentException("Unsupported " + o + " for @Configuration");
            }
        }

        final Collection<org.apache.tomee.embedded.LifecycleTask> lifecycleTasks = new ArrayList<>();
        final Collection<Closeable> postTasks = new ArrayList<>();
        final LifecycleTasks tasks = appClass.getAnnotation(LifecycleTasks.class);
        if (tasks != null) {
            for (final Class<? extends org.apache.tomee.embedded.LifecycleTask> type : tasks.value()) {
                final org.apache.tomee.embedded.LifecycleTask lifecycleTask = type.newInstance();
                lifecycleTasks.add(lifecycleTask);
                postTasks.add(lifecycleTask.beforeContainerStartup());
            }
        }

        final Map<String, Field> ports = new HashMap<>();
        {
            Class<?> type = appClass;
            while (type != null && type != Object.class) {
                for (final Field f : type.getDeclaredFields()) {
                    final RandomPort annotation = f.getAnnotation(RandomPort.class);
                    final String value = annotation == null ? null : annotation.value();
                    if (value != null && value.startsWith("http")) {
                        f.setAccessible(true);
                        ports.put(value, f);
                    }
                }
                type = type.getSuperclass();
            }
        }

        if (ports.containsKey("http")) {
            configuration.randomHttpPort();
        }

        // at least after LifecycleTasks to inherit from potential states (system properties to get a port etc...)
        final Configurers configurers = appClass.getAnnotation(Configurers.class);
        if (configurers != null) {
            for (final Class<? extends Configurer> type : configurers.value()) {
                type.newInstance().configure(configuration);
            }
        }

        final Classes classes = appClass.getAnnotation(Classes.class);
        String context = classes != null ? classes.context() : "";
        context = !context.isEmpty() && context.startsWith("/") ? context.substring(1) : context;

        Archive archive = null;
        if (classes != null && classes.value().length > 0) {
            archive = new ClassesArchive(classes.value());
        }

        final Jars jars = appClass.getAnnotation(Jars.class);
        final List<URL> urls;
        if (jars != null) {
            final Collection<File> files = ApplicationComposers.findFiles(jars);
            urls = new ArrayList<>(files.size());
            for (final File f : files) {
                urls.add(f.toURI().toURL());
            }
        } else {
            urls = null;
        }

        final WebResource resources = appClass.getAnnotation(WebResource.class);
        if (resources != null && resources.value().length > 1) {
            throw new IllegalArgumentException("Only one docBase is supported for now using @WebResource");
        }

        String webResource = null;
        if (resources != null && resources.value().length > 0) {
            webResource = resources.value()[0];
        } else {
            final File webapp = new File("src/main/webapp");
            if (webapp.isDirectory()) {
                webResource = "src/main/webapp";
            }
        }

        if (config != null) { // override other config from annotations
            configuration.loadFromProperties(config);
        }

        final Container container = new Container(configuration);
        SystemInstance.get().setComponent(TomEEEmbeddedArgs.class, new TomEEEmbeddedArgs(args, null));
        SystemInstance.get().setComponent(LifecycleTaskAccessor.class, new LifecycleTaskAccessor(lifecycleTasks));
        container.deploy(new Container.DeploymentRequest(
                context,
                // call ClasspathSearcher that lazily since container needs to be started to not preload logging
                urls == null ? new DeploymentsResolver.ClasspathSearcher().loadUrls(Thread.currentThread().getContextClassLoader()).getUrls() : urls,
                webResource != null ? new File(webResource) : null,
                true,
                null,
                archive));

        for (final Map.Entry<String, Field> f : ports.entrySet()) {
            switch (f.getKey()) {
                case "http":
                    setPortField(f.getKey(), f.getValue(), configuration, context, app);
                    break;
                case "https":
                    break;
                default:
                    throw new IllegalArgumentException("port " + f.getKey() + " not yet supported");
            }
        }

        SystemInstance.get().addObserver(app);
        composerInject(app);

        final AnnotationFinder appFinder = new AnnotationFinder(new ClassesArchive(appClass));
        for (final Method mtd : appFinder.findAnnotatedMethods(PostConstruct.class)) {
            if (mtd.getParameterTypes().length == 0) {
                if (!mtd.isAccessible()) {
                    mtd.setAccessible(true);
                }
                mtd.invoke(app);
            }
        }

        hook = new Thread() {
            @Override
            public void run() { // ensure to log errors but not fail there
                for (final Method mtd : appFinder.findAnnotatedMethods(PreDestroy.class)) {
                    if (mtd.getParameterTypes().length == 0) {
                        if (!mtd.isAccessible()) {
                            mtd.setAccessible(true);
                        }
                        try {
                            mtd.invoke(app);
                        } catch (final IllegalAccessException e) {
                            throw new IllegalStateException(e);
                        } catch (final InvocationTargetException e) {
                            throw new IllegalStateException(e.getCause());
                        }
                    }
                }

                try {
                    container.close();
                } catch (final Exception e) {
                    e.printStackTrace();
                }
                for (final Closeable c : postTasks) {
                    try {
                        c.close();
                    } catch (final IOException e) {
                        e.printStackTrace();
                    }
                }
                postTasks.clear();
                app = null;
                try {
                    SHUTDOWN_TASKS.remove(this);
                } catch (final Exception e) {
                    // no-op: that's ok at that moment if not called manually
                }
            }
        };
        SHUTDOWN_TASKS.put(hook, hook);
    }