/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.server.services.impl; import org.drools.compiler.kie.builder.impl.InternalKieContainer; import org.drools.compiler.kproject.xml.DependencyFilter; import org.kie.api.KieServices; import org.kie.api.builder.Message.Level; import org.kie.api.builder.Results; import org.kie.scanner.KieModuleMetaData; import org.kie.server.api.KieServerConstants; import org.kie.server.api.KieServerEnvironment; import org.kie.server.api.Version; import org.kie.server.api.model.KieContainerResource; import org.kie.server.api.model.KieContainerResourceFilter; import org.kie.server.api.model.KieContainerResourceList; import org.kie.server.api.model.KieContainerStatus; import org.kie.server.api.model.KieScannerResource; import org.kie.server.api.model.KieScannerStatus; import org.kie.server.api.model.KieServerInfo; import org.kie.server.api.model.KieServerStateInfo; import org.kie.server.api.model.Message; import org.kie.server.api.model.ReleaseId; import org.kie.server.api.model.ServiceResponse; import org.kie.server.api.model.ServiceResponse.ResponseType; import org.kie.server.api.model.Severity; import org.kie.server.controller.api.KieServerController; import org.kie.server.controller.api.model.KieServerSetup; import org.kie.server.services.api.KieControllerNotConnectedException; import org.kie.server.services.api.KieControllerNotDefinedException; import org.kie.server.services.api.KieServer; import org.kie.server.services.api.KieServerEventListener; import org.kie.server.services.api.KieServerExtension; import org.kie.server.services.api.KieServerRegistry; import org.kie.server.services.impl.controller.ControllerConnectRunnable; import org.kie.server.services.impl.controller.DefaultRestControllerImpl; import org.kie.server.services.impl.locator.ContainerLocatorProvider; import org.kie.server.services.impl.policy.PolicyManager; import org.kie.server.services.impl.security.JACCIdentityProvider; import org.kie.server.services.impl.storage.KieServerState; import org.kie.server.services.impl.storage.KieServerStateRepository; import org.kie.server.services.impl.storage.file.KieServerStateFileRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.naming.InitialContext; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; public class KieServerImpl implements KieServer { private static final Logger logger = LoggerFactory.getLogger(KieServerImpl.class); private static final ServiceLoader<KieServerExtension> serverExtensions = ServiceLoader.load(KieServerExtension.class); private static final ServiceLoader<KieServerController> kieControllers = ServiceLoader.load(KieServerController.class); // TODO figure out how to get actual URL of the kie server private String kieServerLocation = System.getProperty(KieServerConstants.KIE_SERVER_LOCATION, "http://localhost:8230/kie-server/services/rest/server"); private final KieServerRegistry context; private final PolicyManager policyManager; private final KieServerStateRepository repository; private volatile AtomicBoolean kieServerActive = new AtomicBoolean(false); private List<Message> serverMessages = new ArrayList<Message>(); private Map<String, List<Message>> containerMessages = new ConcurrentHashMap<String, List<Message>>(); private KieServerEventSupport eventSupport = new KieServerEventSupport(); public KieServerImpl() { this(new KieServerStateFileRepository()); } public KieServerImpl(KieServerStateRepository stateRepository) { this.repository = stateRepository; this.context = new KieServerRegistryImpl(); this.context.registerIdentityProvider(new JACCIdentityProvider()); this.context.registerStateRepository(repository); // load available container locators ContainerLocatorProvider.get(); ContainerManager containerManager = getContainerManager(); KieServerState currentState = repository.load(KieServerEnvironment.getServerId()); List<KieServerExtension> extensions = sortKnownExtensions(); for (KieServerExtension extension : extensions) { if (!extension.isActive()) { continue; } try { extension.init(this, this.context); this.context.registerServerExtension(extension); if(extension.isInitialized()) { logger.info("{} has been successfully registered as server extension", extension); } else { logger.warn("{} has not been registered as server extension", extension); } } catch (Exception e) { serverMessages.add(new Message(Severity.ERROR, "Error when initializing server extension of type " + extension + " due to " + e.getMessage())); logger.error("Error when initializing server extension of type {}", extension, e); } } // start policy manager policyManager = new PolicyManager(); policyManager.start(this, context); kieServerActive.set(true); eventSupport.fireBeforeServerStarted(this); boolean readyToRun = false; KieServerController kieController = getController(); // try to load container information from available controllers if any... KieServerInfo kieServerInfo = getInfoInternal(); Set<KieContainerResource> containers = null; KieServerSetup kieServerSetup = null; try { kieServerSetup = kieController.connect(kieServerInfo); containers = kieServerSetup.getContainers(); readyToRun = true; } catch (KieControllerNotDefinedException e) { // if no controllers use local storage containers = currentState.getContainers(); kieServerSetup = new KieServerSetup(); readyToRun = true; } catch (KieControllerNotConnectedException e) { // if controllers are defined but cannot be reached schedule connection and disable until it gets connection to one of them readyToRun = false; logger.warn("Unable to connect to any controllers, delaying container installation until connection can be established"); Thread connectToControllerThread = new Thread(new ControllerConnectRunnable(kieServerActive, kieController, kieServerInfo, currentState, containerManager, this), "KieServer-ControllerConnect"); connectToControllerThread.start(); if (Boolean.parseBoolean(currentState.getConfiguration().getConfigItemValue(KieServerConstants.CFG_SYNC_DEPLOYMENT, "false"))) { logger.info("Containers were requested to be deployed synchronously, holding application start..."); try { connectToControllerThread.join(); } catch (InterruptedException e1) { logger.debug("Interrupt exception when waiting for deployments"); } } } if (readyToRun) { addServerStatusMessage(kieServerInfo); if (Boolean.parseBoolean(currentState.getConfiguration().getConfigItemValue(KieServerConstants.CFG_SYNC_DEPLOYMENT, "false"))) { containerManager.installContainersSync(this, containers, currentState, kieServerSetup); } else { containerManager.installContainers(this, containers, currentState, kieServerSetup); } } eventSupport.fireAfterServerStarted(this); } public KieServerRegistry getServerRegistry() { return context; } public void destroy() { eventSupport.fireBeforeServerStopped(this); kieServerActive.set(false); policyManager.stop(); // disconnect from controller KieServerController kieController = getController(); kieController.disconnect(getInfoInternal()); for (KieServerExtension extension : context.getServerExtensions()) { try { extension.destroy(this, this.context); this.context.unregisterServerExtension(extension); logger.info("{} has been successfully unregistered as server extension", extension); } catch (Exception e) { logger.error("Error when destroying server extension of type {}", extension, e); } } eventSupport.fireAfterServerStopped(this); } public List<KieServerExtension> getServerExtensions() { return this.context.getServerExtensions(); } protected KieServerInfo getInfoInternal() { Version version = KieServerEnvironment.getVersion(); String serverId = KieServerEnvironment.getServerId(); String serverName = KieServerEnvironment.getServerName(); String versionStr = version != null ? version.toString() : "Unknown-Version"; List<String> capabilities = new ArrayList<String>(); for (KieServerExtension extension : context.getServerExtensions()) { capabilities.add(extension.getImplementedCapability()); } return new KieServerInfo(serverId, serverName, versionStr, capabilities, kieServerLocation); } public ServiceResponse<KieServerInfo> getInfo() { try { KieServerInfo kieServerInfo = getInfoInternal(); kieServerInfo.setMessages(serverMessages); return new ServiceResponse<KieServerInfo>(ServiceResponse.ResponseType.SUCCESS, "Kie Server info", kieServerInfo); } catch (Exception e) { logger.error("Error retrieving server info:", e); return new ServiceResponse<KieServerInfo>(ServiceResponse.ResponseType.FAILURE, "Error retrieving kie server info: " + e.getClass().getName() + ": " + e.getMessage()); } } public ServiceResponse<KieContainerResource> createContainer(String containerId, KieContainerResource container) { if (container == null || container.getReleaseId() == null) { logger.error("Error creating container. Release Id is null: " + container); return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.FAILURE, "Failed to create container " + containerId + ". Release Id is null: " + container + "."); } List<Message> messages = new CopyOnWriteArrayList<Message>(); container.setContainerId(containerId); ReleaseId releaseId = container.getReleaseId(); try { KieContainerInstanceImpl ci = new KieContainerInstanceImpl(containerId, KieContainerStatus.CREATING, null, releaseId); ci.getResource().setContainerAlias(container.getContainerAlias()); KieContainerInstanceImpl previous = null; // have to synchronize on the ci or a concurrent call to dispose may create inconsistencies synchronized (ci) { previous = context.registerContainer(containerId, ci); if (previous == null) { try { eventSupport.fireBeforeContainerStarted(this, ci); KieServices ks = KieServices.Factory.get(); InternalKieContainer kieContainer = (InternalKieContainer) ks.newKieContainer(containerId, releaseId); if (kieContainer != null) { ci.setKieContainer(kieContainer); ci.getResource().setConfigItems(container.getConfigItems()); logger.debug("Container {} (for release id {}) general initialization: DONE", containerId, releaseId); Map<String, Object> parameters = getCreateContainerParameters(releaseId); // process server extensions List<KieServerExtension> extensions = context.getServerExtensions(); for (KieServerExtension extension : extensions) { extension.createContainer(containerId, ci, parameters); logger.debug("Container {} (for release id {}) {} initialization: DONE", containerId, releaseId, extension); } if (container.getScanner() != null) { ServiceResponse<KieScannerResource> scannerResponse = configureScanner(containerId, ci, container.getScanner()); if (ResponseType.FAILURE.equals(scannerResponse.getType())) { String errorMessage = "Failed to create scanner for container " + containerId + " with module " + releaseId + "."; messages.add(new Message(Severity.ERROR, errorMessage)); ci.getResource().setStatus(KieContainerStatus.FAILED); return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.FAILURE, errorMessage); } } ci.getResource().setStatus(KieContainerStatus.STARTED); logger.info("Container {} (for release id {}) successfully started", containerId, releaseId); // store the current state of the server KieServerState currentState = repository.load(KieServerEnvironment.getServerId()); container.setStatus(KieContainerStatus.STARTED); currentState.getContainers().add(container); repository.store(KieServerEnvironment.getServerId(), currentState); messages.add(new Message(Severity.INFO, "Container " + containerId + " successfully created with module " + releaseId + ".")); eventSupport.fireAfterContainerStarted(this, ci); return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.SUCCESS, "Container " + containerId + " successfully deployed with module " + releaseId + ".", ci.getResource()); } else { messages.add(new Message(Severity.ERROR, "KieContainer could not be found for release id " + releaseId)); ci.getResource().setStatus(KieContainerStatus.FAILED); ci.getResource().setReleaseId(releaseId); return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.FAILURE, "Failed to create container " + containerId + " with module " + releaseId + "."); } } catch (Exception e) { messages.add(new Message(Severity.ERROR, "Error creating container '" + containerId + "' for module '" + releaseId + "' due to " + e.getMessage())); logger.error("Error creating container '" + containerId + "' for module '" + releaseId + "'", e); ci.getResource().setStatus(KieContainerStatus.FAILED); ci.getResource().setReleaseId(releaseId); return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.FAILURE, "Failed to create container " + containerId + " with module " + releaseId + ": " + e.getClass().getName() + ": " + e.getMessage()); } } else { messages.add(new Message(Severity.ERROR, "Container " + containerId + " already exists.")); return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.FAILURE, "Container " + containerId + " already exists.", previous.getResource()); } } } catch (Exception e) { messages.add(new Message(Severity.ERROR, "Error creating container '" + containerId + "' for module '" + releaseId + "' due to " + e.getMessage())); logger.error("Error creating container '" + containerId + "' for module '" + releaseId + "'", e); return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.FAILURE, "Error creating container " + containerId + " with module " + releaseId + ": " + e.getClass().getName() + ": " + e.getMessage()); } finally { this.containerMessages.put(containerId, messages); } } public ServiceResponse<KieContainerResourceList> listContainers(KieContainerResourceFilter containerFilter) { try { List<KieContainerResource> filteredContainers = new ArrayList<KieContainerResource>(); for (KieContainerResource container : getContainersWithMessages()) { if (containerFilter.accept(container)) { filteredContainers.add(container); } } KieContainerResourceList containerList = new KieContainerResourceList(filteredContainers); return new ServiceResponse<KieContainerResourceList>(ServiceResponse.ResponseType.SUCCESS, "List of created containers", containerList); } catch (Exception e) { logger.error("Error retrieving list of containers", e); return new ServiceResponse<KieContainerResourceList>(ServiceResponse.ResponseType.FAILURE, "Error listing containers: " + e.getClass().getName() + ": " + e.getMessage()); } } private List<KieContainerResource> getContainersWithMessages() { List<KieContainerResource> containers = new ArrayList<KieContainerResource>(); for (KieContainerInstanceImpl instance : context.getContainers()) { instance.getResource().setMessages(getMessagesForContainer(instance.getContainerId())); containers.add(instance.getResource()); } return containers; } public ServiceResponse<KieContainerResource> getContainerInfo(String id) { try { KieContainerInstanceImpl ci = context.getContainer(id); if (ci != null) { setMessages(ci); return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.SUCCESS, "Info for container " + id, ci.getResource()); } return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.FAILURE, "Container " + id + " is not instantiated."); } catch (Exception e) { logger.error("Error retrieving info for container '" + id + "'", e); return new ServiceResponse<KieContainerResource>(ServiceResponse.ResponseType.FAILURE, "Error retrieving container info: " + e.getClass().getName() + ": " + e.getMessage()); } } private void setMessages(KieContainerInstanceImpl kci) { kci.getResource().setMessages(getMessagesForContainer(kci.getContainerId())); } public ServiceResponse<Void> disposeContainer(String containerId) { List<Message> messages = new CopyOnWriteArrayList<Message>(); try { KieContainerInstanceImpl kci = context.unregisterContainer(containerId); if (kci != null) { synchronized (kci) { eventSupport.fireBeforeContainerStopped(this, kci); kci.setStatus(KieContainerStatus.DISPOSING); // just in case if (kci.getKieContainer() != null) { List<KieServerExtension> disposedExtensions = new ArrayList<KieServerExtension>(); try { // first attempt to dispose container on all extensions logger.debug("Container {} (for release id {}) shutdown: In Progress", containerId, kci.getResource().getReleaseId()); // process server extensions List<KieServerExtension> extensions = context.getServerExtensions(); for (KieServerExtension extension : extensions) { extension.disposeContainer(containerId, kci, new HashMap<String, Object>()); logger.debug("Container {} (for release id {}) {} shutdown: DONE", containerId, kci.getResource().getReleaseId(), extension); disposedExtensions.add(extension); } } catch (Exception e) { logger.warn("Dispose of container {} failed, putting it back to started state by recreating container on {}", containerId, disposedExtensions); // since the dispose fail rollback must take place to put it back to running state org.kie.api.builder.ReleaseId releaseId = kci.getKieContainer().getReleaseId(); Map<String, Object> parameters = getCreateContainerParameters(releaseId); for (KieServerExtension extension : disposedExtensions) { extension.createContainer(containerId, kci, parameters); logger.debug("Container {} (for release id {}) {} restart: DONE", containerId, kci.getResource().getReleaseId(), extension); } kci.setStatus(KieContainerStatus.STARTED); context.registerContainer(containerId, kci); logger.info("Container {} (for release id {}) STARTED after failed dispose", containerId, kci.getResource().getReleaseId()); messages.add(new Message(Severity.WARN, "Error disposing container '" + containerId + "' due to " + e.getMessage() + ", container is running")); return new ServiceResponse<Void>(ResponseType.FAILURE, "Container " + containerId + " failed to dispose, exception was raised: " + e.getClass().getName() + ": " + e.getMessage()); } InternalKieContainer kieContainer = kci.getKieContainer(); kci.setKieContainer(null); // helps reduce concurrent access issues // this may fail, but we already removed the container from the registry kieContainer.dispose(); logger.info("Container {} (for release id {}) successfully stopped", containerId, kci.getResource().getReleaseId()); // store the current state of the server KieServerState currentState = repository.load(KieServerEnvironment.getServerId()); List<KieContainerResource> containers = new ArrayList<KieContainerResource>(); for (KieContainerResource containerResource : currentState.getContainers()) { if ( !containerId.equals(containerResource.getContainerId()) ) { containers.add(containerResource); } } currentState.setContainers(new HashSet<KieContainerResource>(containers)); repository.store(KieServerEnvironment.getServerId(), currentState); messages.add(new Message(Severity.INFO, "Container " + containerId + " successfully stopped.")); eventSupport.fireAfterContainerStopped(this, kci); return new ServiceResponse<Void>(ServiceResponse.ResponseType.SUCCESS, "Container " + containerId + " successfully disposed."); } else { messages.add(new Message(Severity.INFO, "Container " + containerId + " was not instantiated.")); return new ServiceResponse<Void>(ServiceResponse.ResponseType.SUCCESS, "Container " + containerId + " was not instantiated."); } } } else { messages.add(new Message(Severity.INFO, "Container " + containerId + " was not instantiated.")); return new ServiceResponse<Void>(ServiceResponse.ResponseType.SUCCESS, "Container " + containerId + " was not instantiated."); } } catch (Exception e) { messages.add(new Message(Severity.ERROR, "Error disposing container '" + containerId + "' due to " + e.getMessage())); logger.error("Error disposing Container '" + containerId + "'", e); return new ServiceResponse<Void>(ServiceResponse.ResponseType.FAILURE, "Error disposing container " + containerId + ": " + e.getClass().getName() + ": " + e.getMessage()); } finally { this.containerMessages.put(containerId, messages); } } public ServiceResponse<KieScannerResource> getScannerInfo(String id) { try { KieContainerInstanceImpl kci = context.getContainer(id); if (kci != null && kci.getKieContainer() != null) { KieScannerResource info = getScannerResource( kci ); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.SUCCESS, "Scanner info successfully retrieved", info); } else { return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Unknown container " + id + "."); } } catch (Exception e) { logger.error("Error retrieving scanner info for container '" + id + "'.", e); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Error retrieving scanner info for container '" + id + "': " + e.getClass().getName() + ": " + e.getMessage()); } } private KieScannerResource getScannerResource(KieContainerInstanceImpl kci) { return kci.getResource().getScanner(); } public ServiceResponse<KieScannerResource> updateScanner(String id, KieScannerResource resource) { if (resource == null || resource.getStatus() == null) { logger.error("Error updating scanner for container " + id + ". Status is null: " + resource); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Error updating scanner for container " + id + ". Status is null: " + resource); } try { KieContainerInstanceImpl kci = context.getContainer(id); if (kci != null && kci.getKieContainer() != null) { // synchronize over the container instance to avoid inconsistent state in case of concurrent updateScanner calls synchronized (kci) { ServiceResponse<KieScannerResource> scannerResponse = configureScanner(id, kci, resource); storeScannerState(kci.getContainerId(), kci.getResource().getScanner()); return scannerResponse; } } else { return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Unknown container " + id + "."); } } catch (Exception e) { logger.error("Error updating scanner for container '" + id + "': " + resource, e); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Error updating scanner for container '" + id + "': " + resource + ": " + e.getClass().getName() + ": " + e.getMessage()); } } private ServiceResponse<KieScannerResource> configureScanner(String containerId, KieContainerInstanceImpl kci, KieScannerResource scannerResource) { KieScannerStatus scannerStatus = scannerResource.getStatus(); ServiceResponse<KieScannerResource> result; switch (scannerStatus) { case CREATED: result = createScanner(containerId, kci); break; case STARTED: result = startScanner(containerId, scannerResource.getPollInterval(), kci); break; case STOPPED: result = stopScanner(containerId, kci); break; case SCANNING: result = scanNow(containerId, kci); break; case DISPOSED: result = disposeScanner(containerId, kci); break; default: // error result = new ServiceResponse<KieScannerResource>(ResponseType.FAILURE, "Unknown status '" + scannerStatus + "' for scanner on container " + containerId + "."); break; } kci.getResource().setScanner(result.getResult()); return result; } /** * Stores (persists) new scanner state for the specified KIE container. * * @param containerId container ID to update the scanner for * @param scannerState new scanner state */ private void storeScannerState(String containerId, KieScannerResource scannerState) { KieServerState currentState = repository.load(KieServerEnvironment.getServerId()); for (KieContainerResource containerResource : currentState.getContainers()) { if (containerId.equals(containerResource.getContainerId())) { containerResource.setScanner(scannerState); } } repository.store(KieServerEnvironment.getServerId(), currentState); } private ServiceResponse<KieScannerResource> startScanner(String id, Long scannerPollInterval, KieContainerInstanceImpl kci) { List<Message> messages = getMessagesForContainer(id); messages.clear(); if (kci.getScanner() == null) { ServiceResponse<KieScannerResource> response = createScanner(id, kci); if (ResponseType.FAILURE.equals(response.getType())) { return response; } } KieScannerStatus scannerStatus = mapScannerStatus(kci); if (KieScannerStatus.STOPPED.equals(scannerStatus) && scannerPollInterval != null) { kci.startScanner(scannerPollInterval); messages.add(new Message(Severity.INFO, "Kie scanner successfully started with interval " + scannerPollInterval)); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.SUCCESS, "Kie scanner successfully created.", getScannerResource(kci)); } else if (!KieScannerStatus.STOPPED.equals(scannerStatus)) { messages.add(new Message(Severity.WARN, "Invalid kie scanner status: " + scannerStatus)); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Invalid kie scanner status: " + scannerStatus, getScannerResource(kci)); } else if (scannerPollInterval == null) { messages.add(new Message(Severity.WARN, "Invalid polling interval: null")); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Invalid polling interval: null", getScannerResource(kci)); } messages.add(new Message(Severity.ERROR, "Unknown error starting scanner. Scanner was not started.")); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Unknown error starting scanner. Scanner was not started.", getScannerResource(kci)); } private ServiceResponse<KieScannerResource> stopScanner(String id, KieContainerInstanceImpl kci) { List<Message> messages = getMessagesForContainer(id); messages.clear(); if (kci.getScanner() == null) { return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.SUCCESS, "Scanner is not started. ", getScannerResource(kci)); } if (KieScannerStatus.STARTED.equals(mapScannerStatus(kci)) || KieScannerStatus.SCANNING.equals(mapScannerStatus(kci))) { kci.stopScanner(); messages.add(new Message(Severity.INFO, "Kie scanner successfully stopped.")); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.SUCCESS, "Kie scanner successfully stopped.", getScannerResource(kci)); } else { KieScannerStatus scannerStatus = mapScannerStatus(kci); messages.add(new Message(Severity.WARN, "Invalid kie scanner status: " + scannerStatus)); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Invalid kie scanner status: " + scannerStatus, getScannerResource(kci)); } } private ServiceResponse<KieScannerResource> scanNow(String id, KieContainerInstanceImpl kci) { List<Message> messages = getMessagesForContainer(id); messages.clear(); if (kci.getScanner() == null) { createScanner( id, kci ); } KieScannerStatus scannerStatus = mapScannerStatus(kci); if (KieScannerStatus.STOPPED.equals( scannerStatus ) || KieScannerStatus.CREATED.equals( scannerStatus ) || KieScannerStatus.STARTED.equals( scannerStatus )) { kci.scanNow(); messages.add(new Message(Severity.INFO, "Kie scanner successfully invoked.")); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.SUCCESS, "Scan successfully executed.", getScannerResource(kci)); } else { messages.add(new Message(Severity.WARN, "Invalid kie scanner status: " + scannerStatus)); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Invalid kie scanner status: " + scannerStatus, getScannerResource(kci)); } } private ServiceResponse<KieScannerResource> disposeScanner(String id, KieContainerInstanceImpl kci) { List<Message> messages = getMessagesForContainer(id); messages.clear(); if (kci.getScanner() == null) { return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.SUCCESS, "Invalid call. Scanner already disposed.", getScannerResource(kci)); } if (KieScannerStatus.STARTED.equals(mapScannerStatus(kci)) || KieScannerStatus.SCANNING.equals(mapScannerStatus(kci))) { ServiceResponse<KieScannerResource> response = stopScanner(id, kci); if (ResponseType.FAILURE.equals(response.getType())) { return response; } } kci.disposeScanner(); messages.add(new Message(Severity.INFO, "Kie scanner successfully disposed (shut down).")); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.SUCCESS, "Kie scanner successfully disposed (shut down).", getScannerResource(kci)); } private KieScannerStatus mapScannerStatus(KieContainerInstanceImpl kieContainerInstance) { return KieContainerInstanceImpl.mapScannerStatus(kieContainerInstance.getScanner().getStatus()); } private ServiceResponse<KieScannerResource> createScanner(String id, KieContainerInstanceImpl kci) { if (kci.getScanner() == null) { List<Message> messages = getMessagesForContainer(id); messages.clear(); kci.createScanner(); messages.add(new Message(Severity.INFO, "Kie scanner successfully created.")); return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.SUCCESS, "Kie scanner successfully created.", getScannerResource(kci)); } else { return new ServiceResponse<KieScannerResource>(ServiceResponse.ResponseType.FAILURE, "Error creating the scanner for container " + id + ". Scanner already exists."); } } public ServiceResponse<ReleaseId> getContainerReleaseId(String id) { try { KieContainerInstanceImpl ci = context.getContainer(id); if (ci != null) { return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.SUCCESS, "ReleaseId for container " + id, ci.getResource().getReleaseId()); } return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.FAILURE, "Container " + id + " is not instantiated."); } catch (Exception e) { logger.error("Error retrieving releaseId for container '" + id + "'", e); return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.FAILURE, "Error retrieving container releaseId: " + e.getClass().getName() + ": " + e.getMessage()); } } public ServiceResponse<ReleaseId> updateContainerReleaseId(String id, ReleaseId releaseId) { if( releaseId == null ) { logger.error("Error updating releaseId for container '" + id + "'. ReleaseId is null."); return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.FAILURE, "Error updating releaseId for container " + id + ". ReleaseId is null. "); } List<Message> messages = getMessagesForContainer(id); messages.clear(); try { KieContainerInstanceImpl kci = context.getContainer(id); // the following code is subject to a concurrent call to dispose(), but the cost of synchronizing it // would likely not be worth it. At this point a decision was made to fail the execution if a concurrent // call do dispose() is executed. if (kci != null && kci.getKieContainer() != null) { // before upgrade check with all extensions if that is allowed KieModuleMetaData metaData = KieModuleMetaData.Factory.newKieModuleMetaData(releaseId, DependencyFilter.COMPILE_FILTER); Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put(KieServerConstants.KIE_SERVER_PARAM_MODULE_METADATA, metaData); // process server extensions List<KieServerExtension> extensions = context.getServerExtensions(); for (KieServerExtension extension : extensions) { boolean allowed = extension.isUpdateContainerAllowed(id, kci, parameters); if (!allowed) { String message = (String)parameters.get(KieServerConstants.FAILURE_REASON_PROP); logger.warn("Container {} (for release id {}) on {} cannot be updated due to {}", id, releaseId, extension, message); if (messages != null) { messages.add(new Message(Severity.WARN, message)); } return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.FAILURE, message); } logger.debug("Container {} (for release id {}) on {} ready to be updated", id, releaseId, extension); } kci.clearExtraClasses(); kci.disposeMarshallers(); Results results = kci.getKieContainer().updateToVersion(releaseId); if (results.hasMessages(Level.ERROR)) { Message error = new Message(Severity.WARN, "Error updating releaseId for container " + id + " to version " + releaseId); for (org.kie.api.builder.Message builderMsg : results.getMessages()) { error.addMessage(builderMsg.getText()); } messages.add(error); logger.error("Error updating releaseId for container " + id + " to version " + releaseId + "\nMessages: " + results.getMessages()); return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.FAILURE, "Error updating release id on container " + id + " to " + releaseId, kci.getResource().getReleaseId()); } else { kci.updateReleaseId(); // once the upgrade was successful, notify all extensions so they can be upgraded (if needed) for (KieServerExtension extension : extensions) { extension.updateContainer(id, kci, parameters); logger.debug("Container {} (for release id {}) on {} updated successfully", id, releaseId, extension); } // store the current state of the server KieServerState currentState = repository.load(KieServerEnvironment.getServerId()); List<KieContainerResource> containers = new ArrayList<KieContainerResource>(); for (KieContainerResource containerResource : currentState.getContainers()) { if ( id.equals(containerResource.getContainerId()) ) { containerResource.setReleaseId(releaseId); containerResource.setResolvedReleaseId(new ReleaseId(kci.getKieContainer().getContainerReleaseId())); } containers.add(containerResource); } currentState.setContainers(new HashSet<KieContainerResource>(containers)); repository.store(KieServerEnvironment.getServerId(), currentState); logger.info("Container {} successfully updated to release id {}", id, releaseId); messages.add(new Message(Severity.INFO, "Release id successfully updated for container " + id)); return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.SUCCESS, "Release id successfully updated.", kci.getResource().getReleaseId()); } } else { // no container yet, attempt to create it KieContainerResource containerResource = new KieContainerResource(id, releaseId, KieContainerStatus.STARTED); ServiceResponse<KieContainerResource> response = createContainer(id, containerResource); if (response.getType().equals(ResponseType.SUCCESS)) { kci = context.getContainer(id); return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.SUCCESS, "Release id successfully updated.", kci.getResource().getReleaseId()); } else { return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.FAILURE, "Container " + id + " is not instantiated."); } } } catch (Exception e) { if (messages != null) { messages.add(new Message(Severity.WARN, "Error updating releaseId for container '" + id + "' due to "+ e.getMessage())); } logger.error("Error updating releaseId for container '" + id + "'", e); return new ServiceResponse<ReleaseId>(ServiceResponse.ResponseType.FAILURE, "Error updating releaseId for container " + id + ": " + e.getClass().getName() + ": " + e.getMessage()); } } public ServiceResponse<KieServerStateInfo> getServerState() { try { KieServerState currentState = repository.load(KieServerEnvironment.getServerId()); KieServerStateInfo state = new KieServerStateInfo(currentState.getControllers(), currentState.getConfiguration(), currentState.getContainers()); return new ServiceResponse<KieServerStateInfo>(ServiceResponse.ResponseType.SUCCESS, "Successfully loaded server state for server id " + KieServerEnvironment.getServerId(), state); } catch (Exception e) { logger.error("Error when loading server state due to {}", e.getMessage(), e); return new ServiceResponse<KieServerStateInfo>(ResponseType.FAILURE, "Error when loading server state due to " + e.getMessage()); } } private Map<String, Object> getCreateContainerParameters(org.kie.api.builder.ReleaseId releaseId) { KieModuleMetaData metaData = KieModuleMetaData.Factory.newKieModuleMetaData(releaseId, DependencyFilter.COMPILE_FILTER); Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put(KieServerConstants.KIE_SERVER_PARAM_MODULE_METADATA, metaData); return parameters; } protected KieServerController getController() { KieServerController controller = new DefaultRestControllerImpl(context); Iterator<KieServerController> it = kieControllers.iterator(); if (it != null && it.hasNext()) { controller = it.next(); } return controller; } protected ContainerManager getContainerManager() { try { return InitialContext.doLookup("java:module/ContainerManagerEJB"); } catch (Exception e) { logger.debug("Unable to find JEE version of ContainerManager suing default one"); return new ContainerManager(); } } protected List<KieServerExtension> sortKnownExtensions() { List<KieServerExtension> extensions = new ArrayList<KieServerExtension>(); for (KieServerExtension extension : serverExtensions) { extensions.add(extension); } Collections.sort(extensions, new Comparator<KieServerExtension>() { @Override public int compare(KieServerExtension e1, KieServerExtension e2) { return e1.getStartOrder().compareTo(e2.getStartOrder()); } }); return extensions; } public void addServerMessage(Message message) { this.serverMessages.add(message); } public void addContainerMessage(String containerId, Message message) { List<Message> messages = getMessagesForContainer(containerId); messages.add(message); } public void addServerStatusMessage(KieServerInfo kieServerInfo) { StringBuilder serverInfoMsg = new StringBuilder(); serverInfoMsg .append("Server ") .append(kieServerInfo) .append("started successfully at ") .append(new Date()); serverMessages.add(new Message(Severity.INFO, serverInfoMsg.toString())); } protected List<Message> getMessagesForContainer(String containerId) { List<Message> messages = containerMessages.get(containerId); if (messages == null) { messages = new CopyOnWriteArrayList<Message>(); containerMessages.put(containerId, messages); } return messages; } public PolicyManager getPolicyManager() { return this.policyManager; } @Override public String toString() { return "KieServer{" + "id='" + KieServerEnvironment.getServerId() + '\'' + "name='" + KieServerEnvironment.getServerName() + '\'' + "version='" + KieServerEnvironment.getVersion() + '\'' + "location='" + kieServerLocation + '\'' + '}'; } }