/* * 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.kie.builder.impl.InternalKieScanner; import org.kie.api.KieServices; import org.kie.server.api.marshalling.Marshaller; import org.kie.server.api.marshalling.MarshallerFactory; import org.kie.server.api.marshalling.MarshallingFormat; import org.kie.server.api.model.KieContainerResource; 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.ReleaseId; import org.kie.server.services.api.KieContainerInstance; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class KieContainerInstanceImpl implements KieContainerInstance { private KieContainerResource resource; private InternalKieContainer kieContainer; private InternalKieScanner scanner; private transient Map<MarshallingFormat, Marshaller> marshallers; private transient Map<String, Object> serviceContainer; private transient Set<Class<?>> extraClasses = new HashSet<Class<?>>(); public KieContainerInstanceImpl(String containerId, KieContainerStatus status) { this(containerId, status, null); } public KieContainerInstanceImpl(String containerId, KieContainerStatus status, InternalKieContainer kieContainer) { this(containerId, status, kieContainer, null); } public KieContainerInstanceImpl(String containerId, KieContainerStatus status, InternalKieContainer kieContainer, ReleaseId releaseId) { super(); this.kieContainer = kieContainer; this.resource = new KieContainerResource(containerId, releaseId, status); // set the default scanner state to DISPOSED (which is the actual default state) // this way we don't need to do null checks all around for the scanner resource this.resource.setScanner(new KieScannerResource(KieScannerStatus.DISPOSED)); this.marshallers = new ConcurrentHashMap<MarshallingFormat, Marshaller>(); this.serviceContainer = new ConcurrentHashMap<String, Object>(); updateReleaseId(); } /** * Maps the {@link InternalKieScanner.Status} to a scanner status used by KIE Server. * * @param status {@link InternalKieScanner.Status} to be converted * * @return {@link KieScannerStatus} which maps to the specified {@link InternalKieScanner.Status} */ public static KieScannerStatus mapScannerStatus(InternalKieScanner.Status status) { switch (status) { case STARTING: return KieScannerStatus.CREATED; case RUNNING: return KieScannerStatus.STARTED; case SCANNING: case UPDATING: return KieScannerStatus.SCANNING; case STOPPED: return KieScannerStatus.STOPPED; case SHUTDOWN: return KieScannerStatus.DISPOSED; default: return KieScannerStatus.UNKNOWN; } } public String getContainerId() { return resource.getContainerId(); } public void setContainerId(String containerId) { this.resource.setContainerId( containerId ); } public InternalKieContainer getKieContainer() { return kieContainer; } public void setKieContainer(InternalKieContainer kieContainer) { this.kieContainer = kieContainer; updateReleaseId(); } public KieContainerStatus getStatus() { return resource.getStatus(); } public void setStatus(KieContainerStatus status) { this.resource.setStatus( status ); } @Override public KieContainerResource getResource() { updateReleaseId(); return resource; } public void setResource(KieContainerResource resource) { this.resource = resource; } public InternalKieScanner getScanner() { return this.scanner; } public void createScanner() { this.scanner = (InternalKieScanner) KieServices.Factory.get().newKieScanner(kieContainer); // we also need to update the underlaying scanner resource to avoid inconsistency KieScannerStatus status = KieContainerInstanceImpl.mapScannerStatus(scanner.getStatus()); long pollingInterval = scanner.getPollingInterval(); resource.setScanner(new KieScannerResource(status, pollingInterval)); } public void startScanner(long pollingInterval) { if (this.scanner == null) { throw new IllegalStateException("Can not start non-existing (null) scanner!"); } this.scanner.start(pollingInterval); this.getResource().setScanner(new KieScannerResource(KieScannerStatus.STARTED, pollingInterval)); } public void scanNow() { if (this.scanner == null) { throw new IllegalStateException("Can not run (scanNow) non-existing (null) scanner!"); } this.scanner.scanNow(); } public void stopScanner() { if (this.scanner == null) { throw new IllegalStateException("Can not stop non-existing (null) scanner!"); } this.scanner.stop(); this.getResource().getScanner().setStatus(KieScannerStatus.STOPPED); } public void disposeScanner() { if (this.scanner == null) { throw new IllegalStateException("Can not dispose non-existing (null) scanner!"); } this.scanner.shutdown(); this.scanner = null; this.getResource().setScanner(new KieScannerResource(KieScannerStatus.DISPOSED)); } public Marshaller getMarshaller(MarshallingFormat format) { synchronized ( marshallers ) { Marshaller marshaller = marshallers.get( format ); if ( marshaller == null ) { marshaller = MarshallerFactory.getMarshaller( getExtraClasses(), format, this.kieContainer.getClassLoader() ); this.marshallers.put( format, marshaller ); } return marshaller; } } public void disposeMarshallers() { synchronized ( marshallers ) { for ( Marshaller marshaller : this.marshallers.values() ) { marshaller.dispose(); } this.marshallers.clear(); } } @Override public void addService(Object service) { if (service == null) { return; } if (serviceContainer.containsKey(service.getClass().getName())) { throw new IllegalStateException("Service " + service.getClass().getName() + " already exists"); } serviceContainer.put(service.getClass().getName(), service); } @Override public boolean addExtraClasses(Set<Class<?>> extraJaxbClassList) { return this.extraClasses.addAll( extraJaxbClassList ); } @Override public void clearExtraClasses() { this.extraClasses.clear(); } @Override public Set<Class<?>> getExtraClasses() { return this.extraClasses; } @Override public <T> T getService(Class<T> serviceType) { return (T) this.serviceContainer.get(serviceType.getName()); } @Override public <T> T removeService(Class<T> serviceType) { return (T) this.serviceContainer.remove(serviceType.getName()); } @Override public String toString() { return resource.toString(); } protected void updateReleaseId() { ReleaseId oldReleaseId = this.resource.getReleaseId(); ReleaseId oldResolvedReleaseId = this.resource.getResolvedReleaseId(); if ( kieContainer != null ) { this.resource.setReleaseId( new ReleaseId( kieContainer.getContainerReleaseId() ) ); this.resource.setResolvedReleaseId( new ReleaseId( kieContainer.getReleaseId() ) ); } // marshallers need to disposed in case the container was updated with different releaseId // proper solution is to attach listener directly to the KieScanner and dispose the marshallers, // but those listeners are not (yet) available, so this is a temporary hackish "solution" if (releaseIdUpdated(oldReleaseId, this.resource.getReleaseId()) || releaseIdUpdated(oldResolvedReleaseId, this.resource.getResolvedReleaseId())) { disposeMarshallers(); } } /** * Checks whether the releaseId was updated (i.e. the old one is different from the new one). * * @param oldReleaseId old ReleaseId * @param newReleaseId new releaseId * @return true if the second (new) releaseId is different and thus was updated; otherwise false */ private boolean releaseIdUpdated(ReleaseId oldReleaseId, ReleaseId newReleaseId) { if (oldReleaseId == null && newReleaseId == null) { return false; } if (oldReleaseId == null && newReleaseId != null) { return true; } // now both releaseIds are non-null, so it is safe to call equals() return !oldReleaseId.equals(newReleaseId); } }