diff --git a/conf/example/Application.xml b/conf/example/Application.xml index 728b42e..3ca9e6e 100755 --- a/conf/example/Application.xml +++ b/conf/example/Application.xml @@ -226,6 +226,16 @@ 2 Integer + + HONEYBADGER_API_KEY + zyx987 + String + + + ENV + example-environment + String + diff --git a/src/edu/stanford/dlss/wowza/SulEnvironment.java b/src/edu/stanford/dlss/wowza/SulEnvironment.java deleted file mode 100644 index 13123cc..0000000 --- a/src/edu/stanford/dlss/wowza/SulEnvironment.java +++ /dev/null @@ -1,9 +0,0 @@ -package edu.stanford.dlss.wowza; - -public class SulEnvironment -{ - public String getEnvironmentVariable(String var) - { - return System.getenv(var); - } -} diff --git a/src/edu/stanford/dlss/wowza/SulWowza.java b/src/edu/stanford/dlss/wowza/SulWowza.java index f736247..98dd1ea 100644 --- a/src/edu/stanford/dlss/wowza/SulWowza.java +++ b/src/edu/stanford/dlss/wowza/SulWowza.java @@ -34,33 +34,20 @@ public class SulWowza extends ModuleBase { static String stacksTokenVerificationBaseUrl; static String stacksUrlErrorMsg = "rejecting due to invalid stacksURL property (" + stacksTokenVerificationBaseUrl + ")"; - static final String HONEYBADGER_API_KEY_ENV_VAR = "WOWZA_HONEYBADGER_API_KEY"; - static final String HONEYBADGER_ENV_NAME_ENV_VAR = "WOWZA_HONEYBADGER_ENV"; static int stacksConnectionTimeout; static int stacksReadTimeout; static NoticeReporter noticeReporter; StandardConfigContext honeybadgerConfig; - SulEnvironment environment; /** configuration is invalid if the stacks url is malformed */ boolean invalidConfiguration = false; - public SulWowza() - {} - - public SulWowza(SulEnvironment se) - { - environment = se; - } - /** invoked when a Wowza application instance is started; * defined in the IModuleOnApp interface */ public void onAppStart(IApplicationInstance appInstance) { - initHoneybadger(); - registerUncaughtExceptionHandler(); - initNoticeReporter(); + initHoneybadger(appInstance); setStacksConnectionTimeout(appInstance); setStacksReadTimeout(appInstance); stacksTokenVerificationBaseUrl = getStacksUrl(appInstance); @@ -147,34 +134,70 @@ public void play(IClient client, RequestFunction function, AMFDataList params) } } + public void setHoneybadgerConfigContext(StandardConfigContext configContext) + { + honeybadgerConfig = configContext; + } + + // TODO: this approach expects the properties to be set in Application.xml + // maybe that's good, or maybe we want to load a java properties file from our plugin jar itself? + // + // TL;DR: Application.xml should contain the API key and the instance environment (e.g. stage, prod), + // in its section. the property names should be "HONEYBADGER_API_KEY" and "ENV", respective. + // see conf/example/Application.xml. + public void initDefaultHoneybadgerConfigContext(IApplicationInstance appInstance) + { + // this is maybe too much background for here and should prob go in a readme or devops doc, but for now... + // the StandardConfigContext constructor first initializes the instance with a DefaultsConfigContext + // by way of a super() call to its parent class constructor (BaseChainedConfigContext). that sets + // the honeybadger API URL. the StandardConfigContext constructor can also take a map of properties, + // from which it will override default property values based on expected field names. in particular, + // it'll look for the environment name first in the "ENV" field, then in the "JAVA_ENV" field. it'll + // look for the API key in the "HONEYBADGER_API_KEY" field, then in the "honeybadger.api_key"* field. + // since IApplicationInstance.getProperties() returns a subclass of Map (WMSProperties) containing properties + // set in Application.xml, we can just set the property names we care about in Application.xml and they'll get + // picked up automatically. + // + // see also: + // https://github.com/honeybadger-io/honeybadger-java#advanced-configuration + // https://github.com/honeybadger-io/honeybadger-java/blob/1.1.0/src/main/java/io/honeybadger/reporter/config/DefaultsConfigContext.java + // https://github.com/honeybadger-io/honeybadger-java/blob/1.1.0/src/main/java/io/honeybadger/reporter/config/BaseChainedConfigContext.java + // https://github.com/honeybadger-io/honeybadger-java/blob/1.1.0/src/main/java/io/honeybadger/reporter/config/StandardConfigContext.java + // https://github.com/honeybadger-io/honeybadger-java/blob/1.1.0/src/main/java/io/honeybadger/reporter/config/MapConfigContext.java + // http://www.wowza.com/resources/serverapi/com/wowza/wms/application/IApplicationInstance.html#getProperties() + // http://www.wowza.com/resources/serverapi/com/wowza/wms/application/WMSProperties.html + // + // * note: i suspect we shouldn't use "honeybadger.api_key", because it appears that + // MapConfigContext.getHoneybadgerUrl() erroneously looks there first for the URL, so we + // should avoid confusing it, to be safe: + // https://github.com/honeybadger-io/honeybadger-java/blob/1.1.0/src/main/java/io/honeybadger/reporter/config/MapConfigContext.java#L113 + setHoneybadgerConfigContext(new StandardConfigContext(appInstance.getProperties())); + } + /** * Initalizes the Honeybadger error reporting tool. This is a public method so we can call * it from the tests. It's outside the constructor, since testing constructors with Mockito is a pain. */ - public void initHoneybadger() + public void initHoneybadger(IApplicationInstance appInstance) { - if(environment == null) - environment = new SulEnvironment(); + initDefaultHoneybadgerConfigContext(appInstance); - String apiKey = environment.getEnvironmentVariable(HONEYBADGER_API_KEY_ENV_VAR); - String honeybadgerEnv = environment.getEnvironmentVariable(HONEYBADGER_ENV_NAME_ENV_VAR); - if(apiKey == null) + if (honeybadgerConfig.getApiKey() == null) { - getLogger().error(this.getClass().getSimpleName() + " unable to set up Honeybadger error reporting (missing API key environment variable?)"); + getLogger().error(this.getClass().getSimpleName() + " unable to set up Honeybadger error reporting (missing API key property in Application.xml?)"); invalidConfiguration = true; } - else if (honeybadgerEnv == null) + else if (honeybadgerConfig.getEnvironment() == null) { - getLogger().error(this.getClass().getSimpleName() + " unable to set up Honeybadger error reporting (missing Honeybadger environment specification?)"); + getLogger().error(this.getClass().getSimpleName() + " unable to set up Honeybadger error reporting (missing Honeybadger environment property in Application.xml?)"); invalidConfiguration = true; } - else - { - honeybadgerConfig = new StandardConfigContext(); - honeybadgerConfig.setApiKey(apiKey) - .setEnvironment(honeybadgerEnv) - .setApplicationPackage(this.getClass().getPackage().getName()); - } + + if (invalidConfiguration) + return; + + honeybadgerConfig.setApplicationPackage(this.getClass().getPackage().getName()); + registerUncaughtExceptionHandler(); } @@ -568,9 +591,14 @@ void registerUncaughtExceptionHandler() HoneybadgerUncaughtExceptionHandler.registerAsUncaughtExceptionHandler(honeybadgerConfig); } + void setNoticeReporter(NoticeReporter noticeReporter) + { + this.noticeReporter = noticeReporter; + } + void initNoticeReporter() { - noticeReporter = new HoneybadgerReporter(honeybadgerConfig); + setNoticeReporter(new HoneybadgerReporter(honeybadgerConfig)); } NoticeReporter getNoticeReporter() diff --git a/test/edu/stanford/dlss/wowza/TestParsingFromRequestInfo.java b/test/edu/stanford/dlss/wowza/TestParsingFromRequestInfo.java index 56084f2..c0fcb01 100644 --- a/test/edu/stanford/dlss/wowza/TestParsingFromRequestInfo.java +++ b/test/edu/stanford/dlss/wowza/TestParsingFromRequestInfo.java @@ -17,7 +17,6 @@ public class TestParsingFromRequestInfo public void setUp() { testModule = new SulWowza(); - testModule.initHoneybadger(); } @Test diff --git a/test/edu/stanford/dlss/wowza/TestSulWowza.java b/test/edu/stanford/dlss/wowza/TestSulWowza.java index 8679ff8..8a66769 100644 --- a/test/edu/stanford/dlss/wowza/TestSulWowza.java +++ b/test/edu/stanford/dlss/wowza/TestSulWowza.java @@ -8,7 +8,10 @@ import org.apache.log4j.*; +import io.honeybadger.reporter.NoticeReporter; +import io.honeybadger.reporter.HoneybadgerReporter; import io.honeybadger.reporter.HoneybadgerUncaughtExceptionHandler; +import io.honeybadger.reporter.config.StandardConfigContext; import com.wowza.wms.amf.AMFDataList; import com.wowza.wms.application.ApplicationInstance; @@ -22,12 +25,16 @@ import java.io.ByteArrayOutputStream; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; public class TestSulWowza { SulWowza testModule; + NoticeReporter mockNoticeReporter; + final static String stacksToken = "encryptedStacksMediaToken"; final static String queryStr = "stacks_token=" + stacksToken; @@ -35,7 +42,8 @@ public class TestSulWowza public void setUp() { testModule = new SulWowza(); - testModule.initHoneybadger(); + mockNoticeReporter = mock(HoneybadgerReporter.class); + testModule.setNoticeReporter(mockNoticeReporter); } @Test @@ -43,8 +51,9 @@ public void onAppStart_calls_setStacksConnectionTimeout() { SulWowza spyModule = spy(testModule); IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); - spyModule.onAppStart(appInstanceMock); + doNothing().when(spyModule).initHoneybadger(appInstanceMock); + spyModule.onAppStart(appInstanceMock); verify(spyModule).setStacksConnectionTimeout(appInstanceMock); } @@ -53,8 +62,9 @@ public void onAppStart_calls_setStacksReadTimeout() { SulWowza spyModule = spy(testModule); IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); - spyModule.onAppStart(appInstanceMock); + doNothing().when(spyModule).initHoneybadger(appInstanceMock); + spyModule.onAppStart(appInstanceMock); verify(spyModule).setStacksReadTimeout(appInstanceMock); } @@ -63,8 +73,9 @@ public void onAppStart_calls_getStacksUrl() { SulWowza spyModule = spy(testModule); IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); - spyModule.onAppStart(appInstanceMock); + doNothing().when(spyModule).initHoneybadger(appInstanceMock); + spyModule.onAppStart(appInstanceMock); verify(spyModule).getStacksUrl(appInstanceMock); } @@ -180,12 +191,38 @@ public void onAppStart_badUrl_callsHoneybadger() } @Test - public void onAppStart_setsUpUncaughtExceptionHandler() + public void initHoneybadger_setsApplicationPackage() + { + SulWowza spyModule = spy(testModule); + IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); + + StandardConfigContext spyHoneybadgerConfig = spy(new StandardConfigContext()); + doReturn("zyx987").when(spyHoneybadgerConfig).getApiKey(); + doReturn("unit_test_env").when(spyHoneybadgerConfig).getEnvironment(); + + spyModule.setHoneybadgerConfigContext(spyHoneybadgerConfig); + doNothing().when(spyModule).initDefaultHoneybadgerConfigContext(appInstanceMock); + doNothing().when(spyModule).registerUncaughtExceptionHandler(); + + spyModule.initHoneybadger(appInstanceMock); + verify(spyHoneybadgerConfig).setApplicationPackage(spyModule.getClass().getPackage().getName()); + } + + @Test + public void initHoneybadger_setsUpUncaughtExceptionHandler() { SulWowza spyModule = spy(testModule); IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); - spyModule.onAppStart(appInstanceMock); + StandardConfigContext mockHoneybadgerConfig = mock(StandardConfigContext.class); + doReturn("zyx987").when(mockHoneybadgerConfig).getApiKey(); + doReturn("unit_test_env").when(mockHoneybadgerConfig).getEnvironment(); + + spyModule.setHoneybadgerConfigContext(mockHoneybadgerConfig); + doNothing().when(spyModule).initDefaultHoneybadgerConfigContext(appInstanceMock); + doNothing().when(spyModule).registerUncaughtExceptionHandler(); + + spyModule.initHoneybadger(appInstanceMock); verify(spyModule).registerUncaughtExceptionHandler(); } @@ -194,52 +231,79 @@ public void onAppStart_initializesHoneybadger() { SulWowza spyModule = spy(testModule); IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); - spyModule.onAppStart(appInstanceMock); + doNothing().when(spyModule).initHoneybadger(appInstanceMock); - verify(spyModule).initHoneybadger(); + spyModule.onAppStart(appInstanceMock); + verify(spyModule).initHoneybadger(appInstanceMock); } @Test public void initHoneybadger_failsWithoutAPIKey() { - SulEnvironment mockSystem = mock(SulEnvironment.class); - when(mockSystem.getEnvironmentVariable(SulWowza.HONEYBADGER_API_KEY_ENV_VAR)).thenReturn(null); - when(mockSystem.getEnvironmentVariable(SulWowza.HONEYBADGER_ENV_NAME_ENV_VAR)).thenReturn("test_env"); + StandardConfigContext mockHoneybadgerConfig = mock(StandardConfigContext.class); + doReturn(null).when(mockHoneybadgerConfig).getApiKey(); + doReturn("unit_test_env").when(mockHoneybadgerConfig).getEnvironment(); - SulWowza localTestModule = new SulWowza(mockSystem); - localTestModule.initHoneybadger(); - assertTrue(localTestModule.invalidConfiguration); + SulWowza spyModule = spy(testModule); + IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); + spyModule.setHoneybadgerConfigContext(mockHoneybadgerConfig); + doNothing().when(spyModule).initDefaultHoneybadgerConfigContext(appInstanceMock); + doNothing().when(spyModule).registerUncaughtExceptionHandler(); + + spyModule.initHoneybadger(appInstanceMock); + assertTrue(spyModule.invalidConfiguration); } @Test public void initHoneybadger_failsWithoutHoneybadgerEnv() { - SulEnvironment mockSystem = mock(SulEnvironment.class); - when(mockSystem.getEnvironmentVariable(SulWowza.HONEYBADGER_API_KEY_ENV_VAR)).thenReturn("abcd"); - when(mockSystem.getEnvironmentVariable(SulWowza.HONEYBADGER_ENV_NAME_ENV_VAR)).thenReturn(null); + StandardConfigContext mockHoneybadgerConfig = mock(StandardConfigContext.class); + doReturn("zyx987").when(mockHoneybadgerConfig).getApiKey(); + doReturn(null).when(mockHoneybadgerConfig).getEnvironment(); - SulWowza localTestModule = new SulWowza(mockSystem); - localTestModule.initHoneybadger(); - assertTrue(localTestModule.invalidConfiguration); + SulWowza spyModule = spy(testModule); + IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); + spyModule.setHoneybadgerConfigContext(mockHoneybadgerConfig); + doNothing().when(spyModule).initDefaultHoneybadgerConfigContext(appInstanceMock); + doNothing().when(spyModule).registerUncaughtExceptionHandler(); + + spyModule.initHoneybadger(appInstanceMock); + assertTrue(spyModule.invalidConfiguration); } @Test public void initHoneybadger_succeedsWithAPIKeyAndHoneybadgerEnv() { - SulEnvironment mockSystem = mock(SulEnvironment.class); - when(mockSystem.getEnvironmentVariable(SulWowza.HONEYBADGER_API_KEY_ENV_VAR)).thenReturn("abcd"); - when(mockSystem.getEnvironmentVariable(SulWowza.HONEYBADGER_ENV_NAME_ENV_VAR)).thenReturn("test_env"); + StandardConfigContext mockHoneybadgerConfig = mock(StandardConfigContext.class); + doReturn("zyx987").when(mockHoneybadgerConfig).getApiKey(); + doReturn("unit_test_env").when(mockHoneybadgerConfig).getEnvironment(); + + SulWowza spyModule = spy(testModule); + IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); + spyModule.setHoneybadgerConfigContext(mockHoneybadgerConfig); + doNothing().when(spyModule).initDefaultHoneybadgerConfigContext(appInstanceMock); + doNothing().when(spyModule).registerUncaughtExceptionHandler(); - SulWowza localTestModule = new SulWowza(mockSystem); - localTestModule.initHoneybadger(); - assertFalse(localTestModule.invalidConfiguration); + spyModule.initHoneybadger(appInstanceMock); + assertFalse(spyModule.invalidConfiguration); } @Test - public void registerUncaughtExceptionHandler_registersHoneybadger() + public void registerUncaughtExceptionHandler_registersHoneybadger() throws URISyntaxException { - testModule.registerUncaughtExceptionHandler(); + StandardConfigContext mockHoneybadgerConfig = mock(StandardConfigContext.class); + doReturn("zyx987").when(mockHoneybadgerConfig).getApiKey(); + doReturn("unit_test_env").when(mockHoneybadgerConfig).getEnvironment(); + doReturn(new URI("https://honey.badger")).when(mockHoneybadgerConfig).getHoneybadgerUrl(); + + SulWowza spyModule = spy(testModule); + IApplicationInstance appInstanceMock = mock(IApplicationInstance.class); + spyModule.setHoneybadgerConfigContext(mockHoneybadgerConfig); + doNothing().when(spyModule).initDefaultHoneybadgerConfigContext(appInstanceMock); + + spyModule.initHoneybadger(appInstanceMock); + spyModule.registerUncaughtExceptionHandler(); HoneybadgerUncaughtExceptionHandler curThreadUncaughtExceptionHandler = (HoneybadgerUncaughtExceptionHandler) (Thread.getDefaultUncaughtExceptionHandler()); @@ -525,7 +589,7 @@ public void authorizeSession_acceptsIfAuthorized() when(sessionMock.getQueryStr()).thenReturn(queryStr); when(sessionMock.getStreamName()).thenReturn(streamName); SulWowza spyModule = spy(testModule); - when(spyModule.verifyStacksToken(stacksToken, druid, filename, userIp)).thenReturn(true); + doReturn(true).when(spyModule).verifyStacksToken(stacksToken, druid, filename, userIp); spyModule.authorizeSession(sessionMock); verify(sessionMock).acceptSession(); @@ -545,7 +609,7 @@ public void authorizeSession_rejectsIfNotAuthorized() when(sessionMock.getQueryStr()).thenReturn(queryStr); when(sessionMock.getStreamName()).thenReturn(streamName); SulWowza spyModule = spy(testModule); - when(spyModule.verifyStacksToken(stacksToken, druid, filename, userIp)).thenReturn(false); + doReturn(false).when(spyModule).verifyStacksToken(stacksToken, druid, filename, userIp); spyModule.authorizeSession(sessionMock); verify(sessionMock).rejectSession(); @@ -702,7 +766,7 @@ public void authorizePlay_trueIfAuthorized() when(spyModule.validateStreamName(streamName)).thenReturn(true); when(spyModule.getDruid(streamName)).thenReturn(druid); when(spyModule.getFilename(streamName)).thenReturn(filename); - when(spyModule.verifyStacksToken(token, druid, filename, userIp)).thenReturn(true); + doReturn(true).when(spyModule).verifyStacksToken(token, druid, filename, userIp); assertEquals(true, spyModule.authorizePlay(queryString, userIp, streamName)); } @@ -724,7 +788,7 @@ public void authorizePlay_falseIfNotAuthorized() when(spyModule.validateStreamName(streamName)).thenReturn(true); when(spyModule.getDruid(streamName)).thenReturn(druid); when(spyModule.getFilename(streamName)).thenReturn(filename); - when(spyModule.verifyStacksToken(token, druid, filename, userIp)).thenReturn(false); + doReturn(false).when(spyModule).verifyStacksToken(token, druid, filename, userIp); assertEquals(false, spyModule.authorizePlay(queryString, userIp, streamName)); } @@ -739,7 +803,7 @@ public void authorizePlay_validatesStacksToken() SulWowza spyModule = spy(testModule); when(spyModule.getStacksToken(queryString)).thenReturn(token); - when(spyModule.validateUserIp("1")).thenReturn(true); + doReturn(true).when(spyModule).validateUserIp("1"); spyModule.authorizePlay(queryString, userIp, streamName); verify(spyModule).validateStacksToken(token); diff --git a/test/edu/stanford/dlss/wowza/TestValidationMethods.java b/test/edu/stanford/dlss/wowza/TestValidationMethods.java index a531de5..216ab7a 100644 --- a/test/edu/stanford/dlss/wowza/TestValidationMethods.java +++ b/test/edu/stanford/dlss/wowza/TestValidationMethods.java @@ -17,7 +17,6 @@ public class TestValidationMethods public void setUp() { testModule = new SulWowza(); - testModule.initHoneybadger(); } @Test diff --git a/test/edu/stanford/dlss/wowza/TestVerifyStacksToken.java b/test/edu/stanford/dlss/wowza/TestVerifyStacksToken.java index 334042f..b0f84ed 100644 --- a/test/edu/stanford/dlss/wowza/TestVerifyStacksToken.java +++ b/test/edu/stanford/dlss/wowza/TestVerifyStacksToken.java @@ -26,7 +26,6 @@ public class TestVerifyStacksToken public void setUp() { testModule = new SulWowza(); - testModule.initHoneybadger(); } @Test