/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License * at: * * http://opensource.org/licenses/ecl2.txt * * 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.opencastproject.serviceregistry.impl; import static org.junit.Assert.assertEquals; import org.opencastproject.job.api.Job; import org.opencastproject.job.api.Job.Status; import org.opencastproject.job.api.JobBarrier; import org.opencastproject.security.api.DefaultOrganization; import org.opencastproject.security.api.JaxbOrganization; import org.opencastproject.security.api.JaxbRole; import org.opencastproject.security.api.JaxbUser; import org.opencastproject.security.api.Organization; import org.opencastproject.security.api.OrganizationDirectoryService; import org.opencastproject.security.api.SecurityService; import org.opencastproject.security.api.TrustedHttpClient; import org.opencastproject.security.api.TrustedHttpClientException; import org.opencastproject.security.api.User; import org.opencastproject.security.api.UserDirectoryService; import org.opencastproject.serviceregistry.api.ServiceRegistration; import org.opencastproject.serviceregistry.api.ServiceRegistryException; import org.opencastproject.systems.MatterhornConstants; import org.opencastproject.util.NotFoundException; import org.opencastproject.util.jmx.JmxUtil; import org.opencastproject.util.persistence.PersistenceUtil; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicStatusLine; import org.easymock.Capture; import org.easymock.EasyMock; import org.easymock.IAnswer; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.beans.PropertyVetoException; import java.util.Date; import java.util.Hashtable; import java.util.concurrent.Executors; import javax.management.ObjectInstance; import javax.persistence.EntityManagerFactory; public class ServiceRegistryJpaImplTest { private static final Logger logger = LoggerFactory.getLogger(ServiceRegistryJpaImplTest.class); private Job undispatchableJob1 = null; private Job undispatchableJob2 = null; private EntityManagerFactory emf = null; private BundleContext bundleContext = null; private ComponentContext cc = null; private ServiceRegistryJpaImpl serviceRegistryJpaImpl = null; private static final String TEST_SERVICE = "ingest"; private static final String TEST_SERVICE_2 = "compose"; private static final String TEST_SERVICE_3 = "org.opencastproject.workflow"; private static final String TEST_OPERATION = "ingest"; private static final String TEST_PATH = "/ingest"; private static final String TEST_PATH_2 = "/compose"; private static final String TEST_PATH_3 = "/workflow"; private static final String TEST_HOST = "http://localhost:8080"; private static final String TEST_HOST_OTHER = "http://otherhost:8080"; @Before public void setUp() throws Exception { // Setup JPA context setUpEntityManagerFactory(); // Setup context settings setupBundleContext(); setupComponentContext(); // Setup test object. setUpServiceRegistryJpaImpl(); } @After public void tearDown() throws ServiceRegistryException { for (ObjectInstance mbean : serviceRegistryJpaImpl.jmxBeans) { JmxUtil.unregisterMXBean(mbean); } for (ServiceRegistration service : serviceRegistryJpaImpl.getServiceRegistrations()) { serviceRegistryJpaImpl.unRegisterService(service.getServiceType(), service.getHost()); } serviceRegistryJpaImpl.deactivate(); } public void setUpUndispatchableJobs() throws ServiceRegistryException { undispatchableJob1 = serviceRegistryJpaImpl.createJob(TEST_HOST, TEST_SERVICE, TEST_OPERATION, null, null, false, null); undispatchableJob2 = serviceRegistryJpaImpl.createJob(TEST_HOST_OTHER, TEST_SERVICE, TEST_OPERATION, null, null, false, null); undispatchableJob1.setDateStarted(new Date()); undispatchableJob1.setStatus(Status.RUNNING); undispatchableJob2.setDateStarted(new Date()); undispatchableJob2.setStatus(Status.RUNNING); undispatchableJob1 = serviceRegistryJpaImpl.updateJob(undispatchableJob1); undispatchableJob2 = serviceRegistryJpaImpl.updateJob(undispatchableJob2); } public void setUpEntityManagerFactory() { emf = PersistenceUtil.newTestEntityManagerFactory("org.opencastproject.common"); } public void setUpServiceRegistryJpaImpl() throws PropertyVetoException, NotFoundException, TrustedHttpClientException { serviceRegistryJpaImpl = new ServiceRegistryJpaImpl(); serviceRegistryJpaImpl.setEntityManagerFactory(emf); Organization organization = new DefaultOrganization(); OrganizationDirectoryService organizationDirectoryService = EasyMock.createMock(OrganizationDirectoryService.class); EasyMock.expect(organizationDirectoryService.getOrganization((String) EasyMock.anyObject())).andReturn(organization) .anyTimes(); EasyMock.replay(organizationDirectoryService); serviceRegistryJpaImpl.setOrganizationDirectoryService(organizationDirectoryService); JaxbOrganization jaxbOrganization = JaxbOrganization.fromOrganization(organization); User anonymous = new JaxbUser("anonymous", "test", jaxbOrganization, new JaxbRole(jaxbOrganization.getAnonymousRole(), jaxbOrganization)); SecurityService securityService = EasyMock.createNiceMock(SecurityService.class); EasyMock.expect(securityService.getUser()).andReturn(anonymous).anyTimes(); EasyMock.expect(securityService.getOrganization()).andReturn(organization).anyTimes(); EasyMock.replay(securityService); serviceRegistryJpaImpl.setSecurityService(securityService); UserDirectoryService userDirectoryService = EasyMock.createNiceMock(UserDirectoryService.class); EasyMock.expect(userDirectoryService.loadUser(EasyMock.anyString())).andReturn(anonymous).anyTimes(); EasyMock.replay(userDirectoryService); serviceRegistryJpaImpl.setUserDirectoryService(userDirectoryService); final Capture<HttpUriRequest> request = EasyMock.newCapture(); final BasicHttpResponse successRespone = new BasicHttpResponse( new BasicStatusLine(new HttpVersion(1, 1), HttpStatus.SC_NO_CONTENT, "No message")); final BasicHttpResponse unavailableResponse = new BasicHttpResponse( new BasicStatusLine(new HttpVersion(1, 1), HttpStatus.SC_SERVICE_UNAVAILABLE, "No message")); TrustedHttpClient trustedHttpClient = EasyMock.createNiceMock(TrustedHttpClient.class); EasyMock.expect(trustedHttpClient.execute(EasyMock.capture(request))).andAnswer(new IAnswer<HttpResponse>() { @Override public HttpResponse answer() throws Throwable { if (!request.hasCaptured()) return unavailableResponse; if (request.getValue().getURI().toString().contains(TEST_PATH)) return unavailableResponse; if (request.getValue().getURI().toString().contains(TEST_PATH_3)) return unavailableResponse; return successRespone; } }).anyTimes(); EasyMock.replay(trustedHttpClient); serviceRegistryJpaImpl.setTrustedHttpClient(trustedHttpClient); } private void registerTestHostAndService() throws ServiceRegistryException { // register the hosts, service must be activated at this point serviceRegistryJpaImpl.registerHost(TEST_HOST, "127.0.0.1", 1024, 1, 1); serviceRegistryJpaImpl.registerHost(TEST_HOST_OTHER, "127.0.0.1", 1024, 1, 2); serviceRegistryJpaImpl.registerService(TEST_SERVICE, TEST_HOST, TEST_PATH); serviceRegistryJpaImpl.registerService(TEST_SERVICE, TEST_HOST_OTHER, TEST_PATH); serviceRegistryJpaImpl.registerService(TEST_SERVICE_2, TEST_HOST, TEST_PATH_2); } private void setupBundleContext() throws InvalidSyntaxException { bundleContext = EasyMock.createNiceMock(BundleContext.class); EasyMock.expect(bundleContext.getProperty(MatterhornConstants.SERVER_URL_PROPERTY)).andReturn(""); EasyMock.expect(bundleContext.getProperty("org.opencastproject.jobs.url")).andReturn(""); EasyMock.expect(bundleContext.getProperty(ServiceRegistryJpaImpl.OPT_MAXLOAD)).andReturn(""); EasyMock.expect(bundleContext.createFilter((String) EasyMock.anyObject())) .andReturn(EasyMock.createNiceMock(Filter.class)); EasyMock.expect(bundleContext.getProperty(ServiceRegistryJpaImpl.OPT_DISPATCHINTERVAL)).andReturn("0"); } private void setupComponentContext() { cc = EasyMock.createMock(ComponentContext.class); EasyMock.expect(cc.getBundleContext()).andReturn(bundleContext).anyTimes(); EasyMock.replay(cc); } @Test public void nullContextActivatesOkay() throws ServiceRegistryException { serviceRegistryJpaImpl.activate(null); } @Test(expected = NotFoundException.class) public void testDeleteJobInvalidJobId() throws Exception { serviceRegistryJpaImpl.activate(null); serviceRegistryJpaImpl.removeJob(-1L); } @Test public void testCancelUndispatchablesOrphanedByActivatingNode() throws Exception { serviceRegistryJpaImpl.activate(null); registerTestHostAndService(); setUpUndispatchableJobs(); // verify the current running status undispatchableJob1 = serviceRegistryJpaImpl.getJob(undispatchableJob1.getId()); assertEquals(Status.RUNNING, undispatchableJob1.getStatus()); undispatchableJob2 = serviceRegistryJpaImpl.getJob(undispatchableJob2.getId()); assertEquals(Status.RUNNING, undispatchableJob2.getStatus()); // remove the activate beans, so this can be reactivated for (ObjectInstance mbean : serviceRegistryJpaImpl.jmxBeans) { JmxUtil.unregisterMXBean(mbean); } // reactivate and expect local undispatchable job to be canceled, but not the remote job serviceRegistryJpaImpl.activate(null); logger.info("Undispatachable job 1 " + undispatchableJob1.getId()); undispatchableJob1 = serviceRegistryJpaImpl.getJob(undispatchableJob1.getId()); assertEquals(Status.CANCELED, undispatchableJob1.getStatus()); logger.info("Undispatachable job 1 " + undispatchableJob2.getId()); undispatchableJob2 = serviceRegistryJpaImpl.getJob(undispatchableJob2.getId()); assertEquals(Status.RUNNING, undispatchableJob2.getStatus()); } @Test public void testHostAddedToPriorityList() throws Exception { if (serviceRegistryJpaImpl.scheduledExecutor != null) serviceRegistryJpaImpl.scheduledExecutor.shutdown(); serviceRegistryJpaImpl.scheduledExecutor = Executors.newScheduledThreadPool(1); serviceRegistryJpaImpl.activate(null); Hashtable<String, String> properties = new Hashtable<>(); properties.put("dispatchinterval", "1000"); serviceRegistryJpaImpl.updated(properties); registerTestHostAndService(); Job testJob = serviceRegistryJpaImpl.createJob(TEST_HOST, TEST_SERVICE, TEST_OPERATION, null, null, true, null); JobBarrier barrier = new JobBarrier(null, serviceRegistryJpaImpl, testJob); try { barrier.waitForJobs(2000); Assert.fail(); } catch (Exception e) { Assert.assertEquals(1, serviceRegistryJpaImpl.dispatchPriorityList.size()); } } @Test public void testHostAddedToPriorityListExceptWorkflowType() throws Exception { if (serviceRegistryJpaImpl.scheduledExecutor != null) serviceRegistryJpaImpl.scheduledExecutor.shutdown(); serviceRegistryJpaImpl.scheduledExecutor = Executors.newScheduledThreadPool(1); serviceRegistryJpaImpl.activate(null); Hashtable<String, String> properties = new Hashtable<>(); properties.put("dispatchinterval", "1000"); serviceRegistryJpaImpl.updated(properties); registerTestHostAndService(); serviceRegistryJpaImpl.registerService(TEST_SERVICE_3, TEST_HOST, TEST_PATH_3); Job testJob = serviceRegistryJpaImpl.createJob(TEST_HOST, TEST_SERVICE_3, TEST_OPERATION, null, null, true, null); JobBarrier barrier = new JobBarrier(null, serviceRegistryJpaImpl, testJob); try { barrier.waitForJobs(2000); Assert.fail(); } catch (Exception e) { Assert.assertEquals(0, serviceRegistryJpaImpl.dispatchPriorityList.size()); } } @Test public void testHostsBeingRemovedFromPriorityList() throws Exception { if (serviceRegistryJpaImpl.scheduledExecutor != null) serviceRegistryJpaImpl.scheduledExecutor.shutdown(); serviceRegistryJpaImpl.scheduledExecutor = Executors.newScheduledThreadPool(1); serviceRegistryJpaImpl.activate(null); Hashtable<String, String> properties = new Hashtable<>(); properties.put("dispatchinterval", "1000"); serviceRegistryJpaImpl.updated(properties); registerTestHostAndService(); serviceRegistryJpaImpl.dispatchPriorityList.put(0L, TEST_HOST); Job testJob = serviceRegistryJpaImpl.createJob(TEST_HOST, TEST_SERVICE_2, TEST_OPERATION, null, null, true, null); JobBarrier barrier = new JobBarrier(null, serviceRegistryJpaImpl, testJob); try { barrier.waitForJobs(2000); Assert.fail(); } catch (Exception e) { Assert.assertEquals(0, serviceRegistryJpaImpl.dispatchPriorityList.size()); } } @Test public void testIgnoreHostsInPriorityList() throws Exception { if (serviceRegistryJpaImpl.scheduledExecutor != null) serviceRegistryJpaImpl.scheduledExecutor.shutdown(); serviceRegistryJpaImpl.scheduledExecutor = Executors.newScheduledThreadPool(1); serviceRegistryJpaImpl.activate(null); Hashtable<String, String> properties = new Hashtable<>(); properties.put("dispatchinterval", "1000"); serviceRegistryJpaImpl.updated(properties); registerTestHostAndService(); Job testJob = serviceRegistryJpaImpl.createJob(TEST_HOST, TEST_SERVICE_2, TEST_OPERATION, null, null, true, null); Job testJob2 = serviceRegistryJpaImpl.createJob(TEST_HOST, TEST_SERVICE, TEST_OPERATION, null, null, true, null); serviceRegistryJpaImpl.dispatchPriorityList.put(testJob2.getId(), TEST_HOST); JobBarrier barrier = new JobBarrier(null, serviceRegistryJpaImpl, testJob, testJob2); try { barrier.waitForJobs(2000); Assert.fail(); } catch (Exception e) { Assert.assertTrue(StringUtils.isBlank(serviceRegistryJpaImpl.getJob(testJob.getId()).getProcessingHost())); Assert.assertTrue(StringUtils.isNotBlank(serviceRegistryJpaImpl.getJob(testJob2.getId()).getProcessingHost())); Assert.assertEquals(1, serviceRegistryJpaImpl.dispatchPriorityList.size()); String blockingHost = serviceRegistryJpaImpl.dispatchPriorityList.get(testJob2.getId()); Assert.assertEquals(TEST_HOST, blockingHost); } } @Test public void testDispatchingJobsHigherMaxLoad() throws Exception { if (serviceRegistryJpaImpl.scheduledExecutor != null) serviceRegistryJpaImpl.scheduledExecutor.shutdown(); serviceRegistryJpaImpl.scheduledExecutor = Executors.newScheduledThreadPool(1); serviceRegistryJpaImpl.activate(null); Hashtable<String, String> properties = new Hashtable<>(); properties.put("dispatchinterval", "1000"); serviceRegistryJpaImpl.updated(properties); registerTestHostAndService(); Job testJob = serviceRegistryJpaImpl.createJob(TEST_HOST, TEST_SERVICE, TEST_OPERATION, null, null, true, null, 10.0f); JobBarrier barrier = new JobBarrier(null, serviceRegistryJpaImpl, testJob); try { barrier.waitForJobs(2000); Assert.fail(); } catch (Exception e) { testJob = serviceRegistryJpaImpl.getJob(testJob.getId()); Assert.assertEquals(TEST_HOST_OTHER, testJob.getProcessingHost()); } } }