org.glassfish.batch.spi.impl.BatchRuntimeHelper Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016-2021] [Payara Foundation and/or affiliates]
package org.glassfish.batch.spi.impl;
import com.ibm.jbatch.container.servicesmanager.ServiceTypes;
import com.ibm.jbatch.spi.*;
import com.sun.enterprise.config.serverbeans.Config;
import fish.payara.jbatch.persistence.rdbms.LazyBootPersistenceManager;
import org.glassfish.api.StartupRunLevel;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.event.EventListener;
import org.glassfish.api.event.EventTypes;
import org.glassfish.api.event.Events;
import org.glassfish.deployment.common.DeploymentContextImpl;
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.runlevel.RunLevel;
import org.glassfish.internal.api.ServerContext;
import org.glassfish.internal.data.ApplicationInfo;
import org.glassfish.internal.data.ApplicationRegistry;
import org.glassfish.internal.data.ModuleInfo;
import org.glassfish.internal.deployment.Deployment;
import org.jvnet.hk2.annotations.Service;
import jakarta.batch.runtime.BatchRuntime;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Helper class to get values for Batch Runtime. Follows zero-config rules by
* using default values when the batch-runtime config object is not present in
* domain.xml
*
* @author Mahesh Kannan
*/
@Service
@RunLevel(StartupRunLevel.VAL)
public class BatchRuntimeHelper
implements PostConstruct, EventListener {
public static final String PAYARA_TABLE_PREFIX_PROPERTY = "payara.jbatch.table.prefix";
public static final String PAYARA_TABLE_SUFFIX_PROPERTY = "payara.jbatch.table.suffix";
@Inject
ServiceLocator serviceLocator;
@Inject
@Named(ServerEnvironment.DEFAULT_INSTANCE_NAME)
private BatchRuntimeConfiguration batchRuntimeConfiguration;
@Inject
private ServerContext serverContext;
private volatile ManagedServiceActivator activator;
@Inject
private GlassFishBatchSecurityHelper glassFishBatchSecurityHelper;
@Inject
private Logger logger;
@Inject
Events events;
@Inject
Config config;
@Inject
ApplicationRegistry applicationRegistry;
private GlassFishBatchExecutorServiceProvider glassFishBatchExecutorServiceProvider
= new GlassFishBatchExecutorServiceProvider();
private AtomicBoolean initialized = new AtomicBoolean(false);
public void checkAndInitializeBatchRuntime() {
if (!initialized.get()) {
synchronized (this) {
if (!initialized.get()) {
initialized.set(true);
}
}
}
}
@Override
public void postConstruct() {
events.register(this);
BatchSPIManager batchSPIManager = BatchSPIManager.getInstance();
batchSPIManager.registerExecutorServiceProvider(glassFishBatchExecutorServiceProvider);
batchSPIManager.registerBatchSecurityHelper(glassFishBatchSecurityHelper);
// setting this puts JBatch into SE mode which is a JBatch bug.
batchSPIManager.registerPlatformMode(BatchSPIManager.PlatformMode.EE);
Properties overrideProperties = new Properties();
overrideProperties.put(PAYARA_TABLE_PREFIX_PROPERTY, batchRuntimeConfiguration.getTablePrefix());
overrideProperties.put(PAYARA_TABLE_SUFFIX_PROPERTY, batchRuntimeConfiguration.getTableSuffix());
overrideProperties.put(ServiceTypes.PERSISTENCE_MANAGEMENT_SERVICE, determinePersistenceManagerClass());
overrideProperties.put(ServiceTypes.CONTAINER_ARTIFACT_FACTORY_SERVICE,"com.ibm.jbatch.container.services.impl.CDIBatchArtifactFactoryImpl" );
overrideProperties.put(ServiceTypes.BATCH_THREADPOOL_SERVICE, "com.ibm.jbatch.container.services.impl.SPIDelegatingThreadPoolServiceImpl");
overrideProperties.put(ServiceTypes.Name.JAVA_EDITION_IS_SE_DUMMY_SERVICE, "false");
batchSPIManager.registerBatchContainerOverrideProperties(overrideProperties);
try {
DatabaseConfigurationBean databaseConfigurationBean = new GlassFishDatabaseConfigurationBean();
databaseConfigurationBean.setSchema(getSchemaName());
batchSPIManager.registerDatabaseConfigurationBean(databaseConfigurationBean);
} catch (DatabaseAlreadyInitializedException daiEx) {
logger.log(Level.SEVERE, null, daiEx);
}
}
public void setExecutorService(ExecutorService executorService) {
glassFishBatchExecutorServiceProvider.setExecutorService(executorService);
}
private Set tagNamesRequiringCleanup = new HashSet<>();
private void registerIfBatchJobsDirExists(ApplicationInfo applicationInfo) {
if (applicationInfo != null && applicationInfo.isLoaded()) {
ClassLoader appClassLoader = applicationInfo.getAppClassLoader();
if (appClassLoader != null && appClassLoader.getResource("META-INF/batch-jobs") != null) {
tagNamesRequiringCleanup.add(config.getName() + ":" + applicationInfo.getName());
return;
}
for (ModuleInfo moduleInfo : applicationInfo.getModuleInfos()) {
ClassLoader moduleClassLoader = moduleInfo.getModuleClassLoader();
if (moduleClassLoader != null && moduleClassLoader.getResource("META-INF/batch-jobs") != null) {
tagNamesRequiringCleanup.add(config.getName() + ":" + applicationInfo.getName());
}
}
}
}
@Override
public void event(Event event) {
try {
if (event.is(EventTypes.SERVER_READY)) {
for (String appName : applicationRegistry.getAllApplicationNames()) {
ApplicationInfo applicationInfo = applicationRegistry.get(appName);
registerIfBatchJobsDirExists(applicationInfo);
}
} else if (event.is(Deployment.APPLICATION_LOADED)) {
if (event.hook() != null && event.hook() instanceof ApplicationInfo) {
ApplicationInfo applicationInfo = (ApplicationInfo) event.hook();
registerIfBatchJobsDirExists(applicationInfo);
}
}
if (event.is(Deployment.UNDEPLOYMENT_SUCCESS)) {
if (event.hook() != null && event.hook() instanceof DeploymentContextImpl) {
DeploymentContextImpl deploymentContext = (DeploymentContextImpl) event.hook();
Properties props = deploymentContext.getAppProps();
String appName = props.getProperty("defaultAppName");
if (!Boolean.parseBoolean(props.getProperty("retain-batch-jobs"))) {
String tagName = config.getName() + ":" + appName;
ClassLoader prevCL = Thread.currentThread().getContextClassLoader();
try {
// set TCCL to ensure loading of the Joboperator
Thread.currentThread().setContextClassLoader(BatchSPIManager.class.getClassLoader());
BatchSPIManager batchSPIManager = BatchSPIManager.getInstance();
if (batchSPIManager.getBatchJobUtil() == null && tagNamesRequiringCleanup.contains(tagName)) {
//Force initialization of BatchRuntime
BatchRuntime.getJobOperator();
}
if (batchSPIManager.getBatchJobUtil() != null) {
batchSPIManager.getBatchJobUtil().purgeOwnedRepositoryData(tagName);
tagNamesRequiringCleanup.remove(tagName);
}
} catch (Exception ex) {
logger.log(Level.FINE, "Error while purging jobs", ex);
} finally {
Thread.currentThread().setContextClassLoader(prevCL);
}
}
}
}
} catch (Exception ex) {
logger.log(Level.FINE, "Exception while handling event: " + event, ex);
}
}
public String getDataSourceLookupName() {
String val = batchRuntimeConfiguration.getDataSourceLookupName();
if (val == null || val.trim().length() == 0) {
val = serverContext.getInstanceName().equals("server")
? "jdbc/__TimerPool" : "jdbc/__default";
}
return val;
}
public static String getDefaultDataSourceLookupNameForTarget(String targetName) {
return targetName.equals("server") ? "jdbc/__TimerPool" : "jdbc/__default";
}
private String getSchemaName() {
return batchRuntimeConfiguration.getSchemaName();
}
public String getExecutorServiceLookupName() {
return batchRuntimeConfiguration.getExecutorServiceLookupName();
}
private String determinePersistenceManagerClass() {
return LazyBootPersistenceManager.class.getName();
}
private class GlassFishDatabaseConfigurationBean extends DatabaseConfigurationBean {
@Override
public String getJndiName() {
checkAndInitializeBatchRuntime();
return getDataSourceLookupName();
}
@Override
public String getSchema() {
checkAndInitializeBatchRuntime();
return getSchemaName();
}
}
private class GlassFishBatchExecutorServiceProvider
implements ExecutorServiceProvider {
private volatile ExecutorService executorService;
void setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
}
@Override
public ExecutorService getExecutorService() {
checkAndInitializeBatchRuntime();
if (executorService == null) {
synchronized (this) {
if (executorService == null) {
if (System.getSecurityManager() == null) {
executorService = lookupExecutorService();
} else {
java.security.AccessController.doPrivileged((java.security.PrivilegedAction) () -> {
executorService = lookupExecutorService();
return null;
});
}
}
}
}
return executorService;
}
}
public ExecutorService lookupExecutorService() {
return lookupExecutorService(getExecutorServiceLookupName());
}
public ExecutorService lookupExecutorService(String exeLookupName) {
try {
if (activator == null) {
activator = serviceLocator.getService(ManagedServiceActivator.class);
}
InitialContext initialContext = new InitialContext();
Object obj = initialContext.lookup(exeLookupName);
if (!(obj instanceof ExecutorService)) {
throw new GlassFishBatchValidationException(exeLookupName + " is not mapped to an ExecutorService. Batch operations may not work correctly.");
}
return (ExecutorService) obj;
} catch (NamingException nEx) {
throw new GlassFishBatchValidationException("No ExecutorService bound to name = " + exeLookupName, nEx);
}
}
}