private Object doCreateResource()

in container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java [3074:3391]


    private Object doCreateResource(final Collection<ServiceInfo> infos, final ResourceInfo serviceInfo) throws OpenEJBException {
        final String skipPropertiesFallback = (String) serviceInfo.properties.remove("SkipPropertiesFallback"); // do it early otherwise we can loose it
        final ObjectRecipe serviceRecipe = createRecipe(infos, serviceInfo);
        final boolean properties = PropertiesFactory.class.getName().equals(serviceInfo.className);
        if ("false".equalsIgnoreCase(serviceInfo.properties.getProperty("SkipImplicitAttributes", "false")) && !properties) {
            serviceRecipe.setProperty("transactionManager", transactionManager);
            serviceRecipe.setProperty("ServiceId", serviceInfo.id);
        }
        serviceInfo.properties.remove("SkipImplicitAttributes");

        // if custom instance allow to skip properties fallback to avoid to set unexpectedly it - connectionProps of DBs
        final AtomicReference<Properties> injectedProperties = new AtomicReference<>();
        if (!"true".equalsIgnoreCase(skipPropertiesFallback)) {
            serviceRecipe.setProperty("properties", new UnsetPropertiesRecipe() {
                @Override
                protected Object internalCreate(final Type expectedType, final boolean lazyRefAllowed) throws ConstructionException {
                    final Map<String, Object> original = serviceRecipe.getUnsetProperties();
                    final Properties properties = new SuperProperties() {
                        @Override
                        public Object remove(final Object key) { // avoid to log them then
                            original.remove(key);
                            return super.remove(key);
                        }
                    }.caseInsensitive(true); // keep our nice case insensitive feature
                    for (final Map.Entry<String, Object> entry : original.entrySet()) {
                        properties.put(entry.getKey(), entry.getValue());
                    }
                    injectedProperties.set(properties);
                    return properties;
                }
            });
        } else { // this is not the best fallback we have but since it is super limited it is acceptable
            final Map<String, Object> unsetProperties = serviceRecipe.getUnsetProperties();
            injectedProperties.set(new Properties() {
                @Override
                public String getProperty(final String key) {
                    final Object obj = unsetProperties.get(key);
                    return String.class.isInstance(obj) ? String.valueOf(obj) : null;
                }

                @Override
                public Set<String> stringPropertyNames() {
                    return unsetProperties.keySet();
                }

                @Override
                public Set<Object> keySet() {
                    //noinspection unchecked
                    return Set.class.cast(unsetProperties.keySet());
                }

                @Override
                public synchronized boolean containsKey(final Object key) {
                    return getProperty(String.valueOf(key)) != null;
                }
            });
        }

        if (serviceInfo.types.contains("DataSource") || serviceInfo.types.contains(DataSource.class.getName())) {
            final Properties props = PropertyPlaceHolderHelper.simpleHolds(serviceInfo.properties);
            if (serviceInfo.properties.containsKey("Definition")) {
                final Object encoding = serviceInfo.properties.remove("DefinitionEncoding");
                try { // we catch classcast etc..., if it fails it is not important
                    final InputStream is = new ByteArrayInputStream(serviceInfo.properties.getProperty("Definition")
                        .getBytes(encoding != null ? encoding.toString() : "ISO-8859-1"));
                    final Properties p = new SuperProperties();
                    IO.readProperties(is, p);
                    for (final Entry<Object, Object> entry : p.entrySet()) {
                        final String key = entry.getKey().toString();
                        if (!props.containsKey(key)
                            // never override from Definition, just use it to complete the properties set
                            &&
                            !(key.equalsIgnoreCase("url") &&
                                props.containsKey("JdbcUrl"))) { // with @DataSource we can get both, see org.apache.openejb.config.ConvertDataSourceDefinitions.rawDefinition()
                            props.put(key, entry.getValue());
                        }
                    }
                } catch (final Exception e) {
                    // ignored
                }
            }
            serviceRecipe.setProperty("Definition", PropertiesHelper.propertiesToString(props));
        } // else: any other kind of resource relying on it? shouldnt be

        replaceResourceAdapterProperty(serviceRecipe);

        ClassLoader loader = Thread.currentThread().getContextClassLoader();

        boolean customLoader = false;
        try {
            if (serviceInfo.classpath != null && serviceInfo.classpath.length > 0) {
                final URL[] urls = new URL[serviceInfo.classpath.length];
                for (int i = 0; i < serviceInfo.classpath.length; i++) {
                    urls[i] = serviceInfo.classpath[i].toURL();
                }
                loader = new URLClassLoaderFirst(urls, loader);
                customLoader = true;
                serviceRecipe.setProperty("OpenEJBResourceClasspath", "true");
            }
        } catch (final MalformedURLException e) {
            throw new OpenEJBException("Unable to create a classloader for " + serviceInfo.id, e);
        }

        if (!customLoader && serviceInfo.classpathAPI != null) {
            throw new IllegalArgumentException("custom-api provided but not classpath used for " + serviceInfo.id);
        }

        Object service = serviceRecipe.create(loader);
        if (customLoader) {
            final Collection<Class<?>> apis;
            if (serviceInfo.classpathAPI == null) {
                apis = new ArrayList<>(Arrays.asList(service.getClass().getInterfaces()));
            } else {
                final String[] split = serviceInfo.classpathAPI.split(" *, *");
                apis = new ArrayList<>(split.length);
                final ClassLoader apiLoader = Thread.currentThread().getContextClassLoader();
                for (final String fqn : split) {
                    try {
                        apis.add(apiLoader.loadClass(fqn));
                    } catch (final ClassNotFoundException e) {
                        throw new IllegalArgumentException(fqn + " not usable as API for " + serviceInfo.id, e);
                    }
                }
            }

            if (apis.size() - (apis.contains(Serializable.class) ? 1 : 0) - (apis.contains(Externalizable.class) ? 1 : 0) > 0) {
                service = Proxy.newProxyInstance(loader, apis.toArray(new Class<?>[apis.size()]), new ClassLoaderAwareHandler(null, service, loader));
            } // else proxy would be useless
        }

        serviceInfo.unsetProperties = injectedProperties.get();

        // Java Connector spec ResourceAdapters and ManagedConnectionFactories need special activation
        if (service instanceof ResourceAdapter) {
            final ResourceAdapter resourceAdapter = (ResourceAdapter) service;

            // Create a thead pool for work manager
            final int threadPoolSize = getIntProperty(serviceInfo.properties, "threadPoolSize", 30);
            final Executor threadPool;
            if (threadPoolSize <= 0) {
                logger.warning("Thread pool for '" + serviceInfo.id + "' is (unbounded), consider setting a size using: " + serviceInfo.id + ".QueueSize=[size]");
                threadPool = Executors.newCachedThreadPool(new DaemonThreadFactory(serviceInfo.id + "-worker-"));
            } else {
                threadPool = new ExecutorBuilder()
                    .size(threadPoolSize)
                    .prefix(serviceInfo.id)
                    .threadFactory(new DaemonThreadFactory(serviceInfo.id + "-worker-"))
                    .build(new Options(serviceInfo.properties, SystemInstance.get().getOptions()));
                logger.info("Thread pool size for '" + serviceInfo.id + "' is (" + threadPoolSize + ")");
            }

            // WorkManager: the resource adapter can use this to dispatch messages or perform tasks
            final WorkManager workManager;
            if (GeronimoTransactionManager.class.isInstance(transactionManager)) {
                final GeronimoTransactionManager geronimoTransactionManager = (GeronimoTransactionManager) transactionManager;
                final TransactionContextHandler txWorkContextHandler = new TransactionContextHandler(geronimoTransactionManager);

                // use id as default realm name if realm is not specified in service properties
                final String securityRealmName = getStringProperty(serviceInfo.properties, "realm", serviceInfo.id);

                final SecurityContextHandler securityContextHandler = new SecurityContextHandler(securityRealmName);
                final HintsContextHandler hintsContextHandler = new HintsContextHandler();

                final Collection<WorkContextHandler> workContextHandlers = new ArrayList<>();
                workContextHandlers.add(txWorkContextHandler);
                workContextHandlers.add(securityContextHandler);
                workContextHandlers.add(hintsContextHandler);

                workManager = new GeronimoWorkManager(threadPool, threadPool, threadPool, workContextHandlers);
            } else {
                workManager = new SimpleWorkManager(threadPool);
            }

            // BootstrapContext: wraps the WorkMananger and XATerminator
            final BootstrapContext bootstrapContext;
            if (transactionManager instanceof GeronimoTransactionManager) {
                bootstrapContext = new GeronimoBootstrapContext(GeronimoWorkManager.class.cast(workManager),
                    (GeronimoTransactionManager) transactionManager,
                    (GeronimoTransactionManager) transactionManager);
            } else if (transactionManager instanceof XATerminator) {
                bootstrapContext = new SimpleBootstrapContext(workManager, (XATerminator) transactionManager);
            } else {
                bootstrapContext = new SimpleBootstrapContext(workManager);
            }

            // start the resource adapter
            try {
                logger.debug("createResource.startingResourceAdapter", serviceInfo.id, service.getClass().getName());
                resourceAdapter.start(bootstrapContext);
            } catch (final ResourceAdapterInternalException e) {
                throw new OpenEJBException(e);
            }

            final Map<String, Object> unset = serviceRecipe.getUnsetProperties();
            unset.remove("threadPoolSize");
            logUnusedProperties(unset, serviceInfo);

            registerAsMBean(serviceInfo.id, "ResourceAdapter", resourceAdapter);
            service = new ResourceAdapterReference(resourceAdapter, threadPool, OPENEJB_RESOURCE_JNDI_PREFIX + serviceInfo.id);
        } else if (service instanceof ManagedConnectionFactory) {
            final ManagedConnectionFactory managedConnectionFactory = (ManagedConnectionFactory) service;

            // connection manager is constructed via a recipe so we automatically expose all cmf properties
            final ObjectRecipe connectionManagerRecipe = new ObjectRecipe(GeronimoConnectionManagerFactory.class, "create");
            connectionManagerRecipe.allow(Option.CASE_INSENSITIVE_PROPERTIES);
            connectionManagerRecipe.allow(Option.IGNORE_MISSING_PROPERTIES);
            connectionManagerRecipe.setAllProperties(serviceInfo.properties);
            connectionManagerRecipe.setProperty("name", serviceInfo.id);
            connectionManagerRecipe.setProperty("mcf", managedConnectionFactory);

            // standard properties
            connectionManagerRecipe.setProperty("transactionManager", transactionManager);
            ClassLoader classLoader = loader;
            if (classLoader == null) {
                classLoader = getClass().getClassLoader();
            }
            if (classLoader == null) {
                classLoader = ClassLoader.getSystemClassLoader();
            }
            connectionManagerRecipe.setProperty("classLoader", classLoader);

            logger.getChildLogger("service").info("createResource.createConnectionManager", serviceInfo.id, service.getClass().getName());

            // create the connection manager
            final ConnectionManager connectionManager = (ConnectionManager) connectionManagerRecipe.create();


            String txSupport = "xa";
            try {
                txSupport = (String) connectionManagerRecipe.getProperty("transactionSupport");
            } catch (Exception e) {
                // ignore
            }

            if (txSupport == null || txSupport.trim().length() == 0) {
                txSupport = "xa";
            }

            if (connectionManager == null) {
                throw new OpenEJBRuntimeException(messages.format("assembler.invalidConnectionManager", serviceInfo.id));
            }

            final Map<String, Object> unsetA = serviceRecipe.getUnsetProperties();
            final Map<String, Object> unsetB = connectionManagerRecipe.getUnsetProperties();
            final Map<String, Object> unset = new HashMap<>();
            for (final Entry<String, Object> entry : unsetA.entrySet()) {
                if (unsetB.containsKey(entry.getKey())) {
                    unset.put(entry.getKey(), entry.getValue());
                }
            }

            // service becomes a ConnectorReference which merges connection manager and mcf
            service = new ConnectorReference(connectionManager, managedConnectionFactory);

            // init cm if needed
            final Object eagerInit = unset.remove("eagerInit");
            if (eagerInit != null && eagerInit instanceof String && "true".equalsIgnoreCase((String) eagerInit)
                && connectionManager instanceof AbstractConnectionManager) {
                try {
                    ((AbstractConnectionManager) connectionManager).doStart();
                    try {
                        final Object cf = managedConnectionFactory.createConnectionFactory(connectionManager);
                        if (cf instanceof ConnectionFactory) {
                            final Connection connection = ((ConnectionFactory) cf).getConnection();
                            connection.getMetaData();
                            connection.close();
                        }
                    } catch (final Exception e) {
                        // no-op: just to force eager init of pool
                    }
                } catch (final Exception e) {
                    logger.warning("Can't start connection manager", e);
                }
            }

            logUnusedProperties(unset, serviceInfo);
        } else if (service instanceof DataSource) {
            ClassLoader classLoader = loader;
            if (classLoader == null) {
                classLoader = getClass().getClassLoader();
            }

            final ImportSql importer = new ImportSql(classLoader, serviceInfo.id, (DataSource) service);
            if (importer.hasSomethingToImport()) {
                importer.doImport();
            }

            final ObjectRecipe recipe = DataSourceFactory.forgetRecipe(service, serviceRecipe);
            if (recipe != serviceRecipe || !serviceInfo.properties.containsKey("XaDataSource")) {
                logUnusedProperties(recipe, serviceInfo);
            } // else logged on xadatasource itself

            final Properties prop = serviceInfo.properties;
            String url = prop.getProperty("JdbcUrl", prop.getProperty("url"));
            if (url == null) {
                url = prop.getProperty("jdbcUrl");
            }
            if (url == null) {
                logger.debug("Unable to find url for " + serviceInfo.id + " will not monitor it");
            } else {
                final String host = extractHost(url);
                if (host != null) {
                    remoteResourceMonitor.addHost(host);
                    remoteResourceMonitor.registerIfNot();
                }
            }
        } else if (!Properties.class.isInstance(service)) {
            if (serviceInfo.unsetProperties == null || isTemplatizedResource(serviceInfo)) {
                logUnusedProperties(serviceRecipe, serviceInfo);
            } // else wait post construct

            registerAsMBean(serviceInfo.id, "Resource", service);
        }

        final ResourceCreated event = new ResourceCreated(service, serviceInfo.id);
        SystemInstance.get().fireEvent(event);
        return event.getReplacement() == null ? service : event.getReplacement();
    }