diff --git a/examples/rollbar-struts2-spring/build.gradle b/examples/rollbar-struts2-spring/build.gradle
new file mode 100755
index 00000000..b36b349d
--- /dev/null
+++ b/examples/rollbar-struts2-spring/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.bmuschko:gradle-tomcat-plugin:2.3'
+ }
+}
+
+apply plugin: 'war'
+apply plugin: 'com.bmuschko.tomcat'
+
+dependencies {
+ implementation project(":rollbar-struts2")
+
+ implementation 'javax.servlet:javax.servlet-api:3.1.0'
+ implementation 'org.apache.struts:struts2-core:2.5.25'
+ implementation 'org.apache.struts:struts2-spring-plugin:2.5.25'
+
+ def tomcatVersion = '7.0.57'
+
+ tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
+ "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
+ tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}")
+}
+
+tomcatRun.contextPath = '/'
+tomcatRunWar.contextPath = '/'
diff --git a/examples/rollbar-struts2-spring/src/main/java/com/example/rollbar/struts2/action/HelloRollbarAction.java b/examples/rollbar-struts2-spring/src/main/java/com/example/rollbar/struts2/action/HelloRollbarAction.java
new file mode 100755
index 00000000..2d438522
--- /dev/null
+++ b/examples/rollbar-struts2-spring/src/main/java/com/example/rollbar/struts2/action/HelloRollbarAction.java
@@ -0,0 +1,38 @@
+package com.example.rollbar.struts2.action;
+
+import com.opensymphony.xwork2.ActionSupport;
+import com.opensymphony.xwork2.inject.Inject;
+import com.rollbar.notifier.Rollbar;
+import com.rollbar.struts.RollbarFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * Struts2 action with Spring CDI.
+ */
+public class HelloRollbarAction extends ActionSupport {
+
+ private static final AtomicInteger counter = new AtomicInteger(1);
+
+ private final Rollbar rollbar;
+
+ @Inject
+ public HelloRollbarAction(RollbarFactory rollbarFactory) {
+ this.rollbar = rollbarFactory.build();
+ }
+
+ public String index() {
+ rollbar.info("Executing index action in HelloRollbarAction");
+
+ return SUCCESS;
+ }
+
+ public String hello() {
+ int current = counter.getAndAdd(1);
+ if (current % 2 == 0) {
+ throw new RuntimeException("Fatal error at hello rollbar action. Number: " + current);
+ }
+ return SUCCESS;
+ }
+
+}
diff --git a/examples/rollbar-struts2-spring/src/main/resources/applicationContext.xml b/examples/rollbar-struts2-spring/src/main/resources/applicationContext.xml
new file mode 100644
index 00000000..490e3a5d
--- /dev/null
+++ b/examples/rollbar-struts2-spring/src/main/resources/applicationContext.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/rollbar-struts2-spring/src/main/resources/struts.xml b/examples/rollbar-struts2-spring/src/main/resources/struts.xml
new file mode 100644
index 00000000..cc2ca45f
--- /dev/null
+++ b/examples/rollbar-struts2-spring/src/main/resources/struts.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hello
+
+
+
+ /WEB-INF/jsp/index.jsp
+
+
+
+
diff --git a/examples/rollbar-struts2-spring/src/main/webapp/WEB-INF/jsp/index.jsp b/examples/rollbar-struts2-spring/src/main/webapp/WEB-INF/jsp/index.jsp
new file mode 100644
index 00000000..0812c2da
--- /dev/null
+++ b/examples/rollbar-struts2-spring/src/main/webapp/WEB-INF/jsp/index.jsp
@@ -0,0 +1,16 @@
+
+
+<%@taglib prefix="s" uri="/struts-tags" %>
+
+
+
+ Hello Rollbar struts2 example application
+
+
+
+ Hello Rollbar!
+
+
+
\ No newline at end of file
diff --git a/examples/rollbar-struts2-spring/src/main/webapp/WEB-INF/web.xml b/examples/rollbar-struts2-spring/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 00000000..f53f5709
--- /dev/null
+++ b/examples/rollbar-struts2-spring/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ contextConfigLocation
+ classpath*:applicationContext*.xml
+
+
+
+ struts2
+ org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
+
+
+ struts2
+ /*
+
+
+
+
+ com.rollbar.web.listener.RollbarRequestListener
+
+
+
+ org.springframework.web.context.ContextLoaderListener
+
+
\ No newline at end of file
diff --git a/examples/rollbar-struts2/build.gradle b/examples/rollbar-struts2/build.gradle
new file mode 100755
index 00000000..0590ea86
--- /dev/null
+++ b/examples/rollbar-struts2/build.gradle
@@ -0,0 +1,28 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.bmuschko:gradle-tomcat-plugin:2.3'
+ }
+}
+
+apply plugin: 'war'
+apply plugin: 'com.bmuschko.tomcat'
+
+dependencies {
+ implementation project(":rollbar-struts2")
+
+ implementation 'org.apache.struts:struts2-core:2.5.25'
+ implementation 'javax.servlet:javax.servlet-api:3.1.0'
+
+ def tomcatVersion = '7.0.57'
+
+ tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
+ "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
+ tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}")
+}
+
+tomcatRun.contextPath = '/'
+tomcatRunWar.contextPath = '/'
diff --git a/examples/rollbar-struts2/src/main/java/com/example/rollbar/struts2/action/HelloRollbarAction.java b/examples/rollbar-struts2/src/main/java/com/example/rollbar/struts2/action/HelloRollbarAction.java
new file mode 100755
index 00000000..56cce5d3
--- /dev/null
+++ b/examples/rollbar-struts2/src/main/java/com/example/rollbar/struts2/action/HelloRollbarAction.java
@@ -0,0 +1,23 @@
+package com.example.rollbar.struts2.action;
+
+import com.opensymphony.xwork2.ActionSupport;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * Struts2 action that rises an error every even request number.
+ */
+public class HelloRollbarAction extends ActionSupport {
+
+ private static final AtomicInteger counter = new AtomicInteger(1);
+
+ public String execute() {
+ int current = counter.getAndAdd(1);
+ if (current % 2 == 0) {
+ throw new RuntimeException("Fatal error at hello rollbar action. Number: " + current);
+ }
+ return SUCCESS;
+ }
+
+
+}
diff --git a/examples/rollbar-struts2/src/main/resources/struts.xml b/examples/rollbar-struts2/src/main/resources/struts.xml
new file mode 100644
index 00000000..c4fea7ff
--- /dev/null
+++ b/examples/rollbar-struts2/src/main/resources/struts.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+ [ACCESS_TOKEN]
+ true
+ production
+ 1.1.2
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /WEB-INF/jsp/index.jsp
+
+
+
+
diff --git a/examples/rollbar-struts2/src/main/webapp/WEB-INF/jsp/index.jsp b/examples/rollbar-struts2/src/main/webapp/WEB-INF/jsp/index.jsp
new file mode 100644
index 00000000..0812c2da
--- /dev/null
+++ b/examples/rollbar-struts2/src/main/webapp/WEB-INF/jsp/index.jsp
@@ -0,0 +1,16 @@
+
+
+<%@taglib prefix="s" uri="/struts-tags" %>
+
+
+
+ Hello Rollbar struts2 example application
+
+
+
+ Hello Rollbar!
+
+
+
\ No newline at end of file
diff --git a/examples/rollbar-struts2/src/main/webapp/WEB-INF/web.xml b/examples/rollbar-struts2/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 00000000..2e6f0950
--- /dev/null
+++ b/examples/rollbar-struts2/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ com.rollbar.web.listener.RollbarRequestListener
+
+
+
+ struts2
+ org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
+
+
+ struts2
+ /*
+
+
\ No newline at end of file
diff --git a/rollbar-struts2/build.gradle b/rollbar-struts2/build.gradle
new file mode 100644
index 00000000..a384708c
--- /dev/null
+++ b/rollbar-struts2/build.gradle
@@ -0,0 +1,8 @@
+ext {
+ struts2Version = '2.5.25'
+}
+
+dependencies {
+ api project(":rollbar-web")
+ implementation 'org.apache.struts:struts2-core:' + struts2Version
+}
\ No newline at end of file
diff --git a/rollbar-struts2/src/main/java/com/rollbar/struts/RollbarFactory.java b/rollbar-struts2/src/main/java/com/rollbar/struts/RollbarFactory.java
new file mode 100644
index 00000000..050ede84
--- /dev/null
+++ b/rollbar-struts2/src/main/java/com/rollbar/struts/RollbarFactory.java
@@ -0,0 +1,103 @@
+package com.rollbar.struts;
+
+import static com.rollbar.notifier.config.ConfigBuilder.withAccessToken;
+
+import com.rollbar.notifier.Rollbar;
+import com.rollbar.notifier.config.Config;
+import com.rollbar.notifier.config.ConfigBuilder;
+import com.rollbar.notifier.config.ConfigProvider;
+import com.rollbar.notifier.config.ConfigProviderHelper;
+import com.rollbar.web.provider.PersonProvider;
+import com.rollbar.web.provider.RequestProvider;
+
+public class RollbarFactory {
+
+ private String accessToken;
+
+ private String userIpHeader;
+
+ private String captureIp;
+
+ private String environment;
+
+ private String codeVersion;
+
+ private String configProviderClassName;
+
+ public RollbarFactory() {
+ this(null, null, null, null, null, null);
+ }
+
+ /**
+ * Constructor.
+ * @param accessToken the rollbar access token.
+ * @param userIpHeader request http header name used for getting user ip.
+ * @param captureIp "true" or "false" values to capture and send user ip.
+ * @param environment the environment.
+ * @param codeVersion the code version.
+ * @param configProviderClassName the class name used to use a custom configuration.
+ */
+ public RollbarFactory(String accessToken, String userIpHeader, String captureIp,
+ String environment, String codeVersion, String configProviderClassName) {
+ this.accessToken = accessToken;
+ this.userIpHeader = userIpHeader;
+ this.captureIp = captureIp;
+ this.environment = environment;
+ this.codeVersion = codeVersion;
+ this.configProviderClassName = configProviderClassName;
+ }
+
+ /**
+ * Build method to create a Rollbar object instance.
+ * @return the rollbar instance.
+ */
+ public Rollbar build() {
+ ConfigProvider configProvider = ConfigProviderHelper.getConfigProvider(configProviderClassName);
+ Config config;
+
+ RequestProvider requestProvider = new RequestProvider.Builder()
+ .userIpHeaderName(userIpHeader)
+ .captureIp(captureIp)
+ .build();
+
+ ConfigBuilder configBuilder = withAccessToken(accessToken)
+ .request(requestProvider)
+ .person(new PersonProvider())
+ .environment(environment)
+ .codeVersion(codeVersion);
+
+ if (configProvider != null) {
+ config = configProvider.provide(configBuilder);
+ } else {
+ config = configBuilder.build();
+ }
+
+ return new Rollbar(config);
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+
+ public void setUserIpHeader(String userIpHeader) {
+ this.userIpHeader = userIpHeader;
+ }
+
+ public void setConfigProviderClassName(String configProviderClassName) {
+ this.configProviderClassName = configProviderClassName;
+ }
+
+ public void setCaptureIp(String captureIp) {
+ this.captureIp = captureIp;
+ }
+
+ public void setEnvironment(String environment) {
+ this.environment = environment;
+ }
+
+ public void setCodeVersion(String codeVersion) {
+ this.codeVersion = codeVersion;
+ }
+
+}
diff --git a/rollbar-struts2/src/main/java/com/rollbar/struts/interceptor/RollbarExceptionInterceptor.java b/rollbar-struts2/src/main/java/com/rollbar/struts/interceptor/RollbarExceptionInterceptor.java
new file mode 100644
index 00000000..cfbf7169
--- /dev/null
+++ b/rollbar-struts2/src/main/java/com/rollbar/struts/interceptor/RollbarExceptionInterceptor.java
@@ -0,0 +1,99 @@
+package com.rollbar.struts.interceptor;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.rollbar.notifier.Rollbar;
+import com.rollbar.struts.RollbarFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RollbarExceptionInterceptor extends AbstractInterceptor {
+
+ private static Logger LOGGER = LoggerFactory.getLogger(RollbarExceptionInterceptor.class);
+
+ private String accessToken;
+
+ private String userIpHeader;
+
+ private String captureIp;
+
+ private String environment;
+
+ private String codeVersion;
+
+ private String configProviderClassName;
+
+ private Rollbar rollbar;
+
+ public RollbarExceptionInterceptor() {
+ this(null);
+ }
+
+ /**
+ * Constructor used with CI.
+ * @param rollbarFactory the rollbar factory.
+ */
+ @Inject
+ public RollbarExceptionInterceptor(RollbarFactory rollbarFactory) {
+ if (rollbarFactory != null) {
+ this.rollbar = rollbarFactory.build();
+ } else {
+ this.rollbar = null;
+ }
+ }
+
+ @Override
+ public void init() {
+ if (rollbar != null) {
+ LOGGER.info("Rollbar instance found by Dependency Injection!. "
+ + "Ignoring interceptor properties.");
+ return;
+ }
+
+ LOGGER.debug("Rollbar instance not found by Dependency Injection!. "
+ + "Using interceptor properties.");
+
+ RollbarFactory rollbarFactory = new RollbarFactory(accessToken, userIpHeader, captureIp,
+ environment, codeVersion, configProviderClassName);
+ this.rollbar = rollbarFactory.build();
+ }
+
+ @Override
+ public String intercept(ActionInvocation actionInvocation) throws Exception {
+ try {
+ return actionInvocation.invoke();
+ } catch (Exception e) {
+ rollbar.error(e);
+ throw e;
+ }
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+ public void setUserIpHeader(String userIpHeader) {
+ this.userIpHeader = userIpHeader;
+ }
+
+ public void setConfigProviderClassName(String configProviderClassName) {
+ this.configProviderClassName = configProviderClassName;
+ }
+
+ public void setCaptureIp(String captureIp) {
+ this.captureIp = captureIp;
+ }
+
+ public void setEnvironment(String environment) {
+ this.environment = environment;
+ }
+
+ public void setCodeVersion(String codeVersion) {
+ this.codeVersion = codeVersion;
+ }
+
+ Rollbar getRollbar() {
+ return this.rollbar;
+ }
+}
diff --git a/rollbar-struts2/src/test/java/com/rollbar/struts/RollbarFactoryTest.java b/rollbar-struts2/src/test/java/com/rollbar/struts/RollbarFactoryTest.java
new file mode 100644
index 00000000..a0dd4af7
--- /dev/null
+++ b/rollbar-struts2/src/test/java/com/rollbar/struts/RollbarFactoryTest.java
@@ -0,0 +1,16 @@
+package com.rollbar.struts;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+public class RollbarFactoryTest {
+
+ @Test
+ public void testBuild() {
+ RollbarFactory sut = new RollbarFactory();
+
+ assertNotNull(sut.build());
+ }
+
+}
diff --git a/rollbar-struts2/src/test/java/com/rollbar/struts/interceptor/RollbarExceptionInterceptorTest.java b/rollbar-struts2/src/test/java/com/rollbar/struts/interceptor/RollbarExceptionInterceptorTest.java
new file mode 100644
index 00000000..595dc9be
--- /dev/null
+++ b/rollbar-struts2/src/test/java/com/rollbar/struts/interceptor/RollbarExceptionInterceptorTest.java
@@ -0,0 +1,79 @@
+package com.rollbar.struts.interceptor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.rollbar.notifier.Rollbar;
+import com.rollbar.struts.RollbarFactory;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+public class RollbarExceptionInterceptorTest {
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ private RollbarExceptionInterceptor sut;
+
+ @Mock
+ private RollbarFactory rollbarFactory;
+
+ @Mock
+ private Rollbar rollbar;
+
+ @Mock
+ private ActionInvocation action;
+
+ @Before
+ public void setUp() {
+ when(rollbarFactory.build()).thenReturn(rollbar);
+
+ sut = new RollbarExceptionInterceptor(rollbarFactory);
+ }
+
+ @Test
+ public void testInitWithDI() throws Exception {
+ sut.init();
+
+ assertEquals(rollbar, sut.getRollbar());
+ }
+
+ @Test
+ public void testInitWithoutDI() throws Exception {
+ String accessToken = "test_access_token";
+
+ sut = new RollbarExceptionInterceptor();
+ sut.setAccessToken(accessToken);
+
+ sut.init();
+
+ assertNotNull(sut.getRollbar());
+ }
+
+ @Test
+ public void testIntercept() throws Exception {
+ Exception exception = mock(Exception.class);
+
+ when(action.invoke()).thenThrow(exception);
+
+ sut.init();
+
+ try {
+ sut.intercept(action);
+ } catch (Exception e) {
+ if(!e.equals(exception)) {
+ fail();
+ }
+ }
+ verify(rollbar).error(exception);
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index e88435bd..56e609a9 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -8,6 +8,7 @@ include ":rollbar-api",
":rollbar-logback",
":rollbar-spring-webmvc",
":rollbar-spring-boot-webmvc",
+ ":rollbar-struts2",
":examples:rollbar-java",
":examples:rollbar-web",
":examples:rollbar-android",
@@ -15,4 +16,7 @@ include ":rollbar-api",
":examples:rollbar-log4j2",
":examples:rollbar-logback",
":examples:rollbar-spring-webmvc",
- ":examples:rollbar-spring-boot-webmvc"
+ ":examples:rollbar-spring-boot-webmvc",
+ ":examples:rollbar-struts2",
+ ":examples:rollbar-struts2-spring"
+