Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extract Servlet functionality into sse-common module for reuse #23

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5a0e213
extract Servlet dependencies into shared class
jordanglassman Sep 18, 2017
8a3de16
split plugin into common and plugin modules
jordanglassman Sep 19, 2017
9c9ed29
add .gitignored file
jordanglassman Sep 19, 2017
22ff745
update sample project to use refactored plugin
jordanglassman Sep 19, 2017
08d0942
add new unit tests for base class
jordanglassman Sep 19, 2017
03153da
extract js client into standalone module
jordanglassman Sep 19, 2017
00bea67
rename modules to be consistent with existing project structure
jordanglassman Sep 19, 2017
55c2897
add .gitignored file
jordanglassman Sep 19, 2017
9a6b96d
fix accidental reversion
jordanglassman Sep 19, 2017
d8bba61
update package.json to use local copy of client
jordanglassman Sep 20, 2017
46f2abe
remove local copy of sse-gateway on clean
jordanglassman Sep 20, 2017
77306c4
expose apis needed for reuse
jordanglassman Sep 20, 2017
2a6edc5
move sse-gateway dep into devDependencies
jordanglassman Sep 21, 2017
d5facf8
update poms and deps per code review comments
jordanglassman Sep 21, 2017
fdce9f8
expose additional subscription queue apis
jordanglassman Sep 21, 2017
c0549a9
add repo for resolving parent pom
jordanglassman Sep 21, 2017
0c00c3f
add EventFilter subclass to maintain backwards compatibility with cli…
jordanglassman Sep 28, 2017
e29206a
refactor to use refactored version of pubsub-light with no jenkins de…
jordanglassman Sep 28, 2017
ac1638a
add sse-servlet to sse-gateway project
jordanglassman Sep 28, 2017
84fd556
updates following reversion of pubsub light repackaging
jordanglassman Oct 5, 2017
817d849
update get endpoints to correctly handle missing paths and hyphens
jordanglassman Oct 5, 2017
cf2d60a
updates to allow for serialization
jordanglassman Nov 2, 2017
a097c1b
extract sse servlet into module
jordanglassman Nov 2, 2017
ab3f623
add redis config provider
jordanglassman Nov 2, 2017
e3ee6b0
minor IT adjustments
jordanglassman Nov 2, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
</pluginRepository>
</pluginRepositories>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
Expand All @@ -48,8 +52,8 @@
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>pubsub-light</artifactId>
<version>1.6</version>
<artifactId>pubsub-light-common</artifactId>
<version>1.13-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.main</groupId>
Expand All @@ -61,6 +65,26 @@
<artifactId>annotations</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>json-lib</artifactId>
<version>2.4-jenkins-2</version>
</dependency>
<dependency>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security</artifactId>
<version>1.0.7</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>11.0.1</version>
</dependency>

<!-- Test deps -->
<dependency>
Expand All @@ -85,12 +109,15 @@
<module>sse-common</module>
<module>sse-gateway-client</module>
<module>sse-gateway</module>
<module>sse-servlet</module>
</modules>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
Expand Down
18 changes: 15 additions & 3 deletions sse-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,28 @@
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>pubsub-light</artifactId>
<artifactId>pubsub-light-common</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>jenkins-core</artifactId>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>json-lib</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>annotations</artifactId>
</dependency>
<dependency>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<!-- Test deps -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
*/
package org.jenkinsci.plugins.ssegateway;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.apache.commons.io.FileUtils;
import org.jenkinsci.plugins.pubsub.ChannelSubscriber;
import org.jenkinsci.plugins.pubsub.PubsubBus;
import org.jenkinsci.plugins.pubsub.message.Message;
import org.jenkinsci.plugins.ssegateway.sse.EventDispatcher;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.io.File;
Expand All @@ -39,16 +47,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.security.ACL;
import org.apache.commons.io.FileUtils;
import org.jenkinsci.plugins.pubsub.ChannelSubscriber;
import org.jenkinsci.plugins.pubsub.Message;
import org.jenkinsci.plugins.pubsub.PubsubBus;
import org.jenkinsci.plugins.ssegateway.sse.EventDispatcher;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

/**
* Channel event message history store.
* <p>
Expand All @@ -57,11 +55,11 @@
*
* @author <a href="mailto:[email protected]">[email protected]</a>
*/
@Restricted(NoExternalUse.class)
public final class EventHistoryStore {

private static final Logger LOGGER = Logger.getLogger(EventHistoryStore.class.getName());


private static final String SYSTEM_USERNAME = "SYSTEM";

private static File historyRoot;
private static long expiresAfter = (1000 * 60); // default of 1 minutes
private static Map<String, File> channelDirs = new ConcurrentHashMap<>();
Expand All @@ -75,7 +73,7 @@ public final class EventHistoryStore {
public static void setHistoryRoot(@Nonnull File historyRoot) throws IOException {
// In a non-test mode, we only allow setting of the historyRoot
// once (during plugin init - see Endpoint class).
if (EventHistoryStore.historyRoot != null && !Util.isTestEnv()) {
if (EventHistoryStore.historyRoot != null && !Util.isTestEnv(null)) {
LOGGER.log(Level.SEVERE, "Invalid attempt to change historyRoot after it has already been set. Ignoring.");
return;
}
Expand All @@ -90,7 +88,7 @@ public static void setHistoryRoot(@Nonnull File historyRoot) throws IOException

static void setExpiryMillis(long expiresAfterMillis) {
// In a non-test mode, we don't allow setting of the expiresAfter at all.
if (!Util.isTestEnv()) {
if (!Util.isTestEnv(null)) {
LOGGER.log(Level.SEVERE, "Invalid attempt to change expiresAfterMillis. Ignoring.");
return;
}
Expand Down Expand Up @@ -289,7 +287,7 @@ private static synchronized AtomicInteger newChannelSubsCounter(@Nonnull String
EventHistoryLogger logger = channelLoggers.get(channelName);
if (logger == null) {
logger = new EventHistoryLogger(counter);
PubsubBus.getBus().subscribe(channelName, logger, ACL.SYSTEM, null);
PubsubBus.getBus().subscribe(channelName, logger, new UsernamePasswordAuthenticationToken(SYSTEM_USERNAME,"SYSTEM"), null);
channelLoggers.put(channelName, logger);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jenkinsci.plugins.ssegateway;

import org.acegisecurity.Authentication;
import org.jenkinsci.plugins.ssegateway.sse.EventDispatcher;
import org.jenkinsci.plugins.ssegateway.sse.EventDispatcherFactory;

Expand All @@ -22,7 +23,7 @@ public class SseServletBase {
*
* @throws IOException
*/
public void initDispatcher(HttpServletRequest request, HttpServletResponse response) throws IOException {
public void initDispatcher(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
String clientId = request.getParameter("clientId");

if (clientId == null) {
Expand All @@ -39,7 +40,7 @@ public void initDispatcher(HttpServletRequest request, HttpServletResponse respo
dispatcher.unsubscribeAll();
} else {
// Else create a new instance with this id.
EventDispatcherFactory.newDispatcher(clientId, session);
EventDispatcherFactory.newDispatcher(clientId, session, authentication);
}

response.setStatus(HttpServletResponse.SC_OK);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@
import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.jenkinsci.plugins.pubsub.EventFilter;
import org.jenkinsci.plugins.ssegateway.message.SubscriptionConfigEventFilter;
import org.jenkinsci.plugins.ssegateway.sse.EventDispatcher;
import org.jenkinsci.plugins.ssegateway.sse.EventDispatcherFactory;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
Expand All @@ -46,7 +44,6 @@
/**
* @author <a href="mailto:[email protected]">[email protected]</a>
*/
@Restricted(NoExternalUse.class)
public final class SubscriptionConfigQueue {

private static final Logger LOGGER = Logger.getLogger(SubscriptionConfigQueue.class.getName());
Expand Down Expand Up @@ -128,12 +125,14 @@ private static void doConfigure(SubscriptionConfig subscriptionConfig) {
if (subscriptionConfig.unsubscribeAll) {
dispatcher.unsubscribeAll();
}
for (EventFilter filter : subscriptionConfig.unsubscribeSet) {
for (SubscriptionConfigEventFilter filter : subscriptionConfig.unsubscribeSet) {
LOGGER.log(Level.FINER, "processing unsubscribe request for subscriptionConfig={0}", filter);
if (dispatcher.unsubscribe(filter)) {
EventHistoryStore.onChannelUnsubscribe(filter.getChannelName());
}
}
for (EventFilter filter : subscriptionConfig.subscribeSet) {
for (SubscriptionConfigEventFilter filter : subscriptionConfig.subscribeSet) {
LOGGER.log(Level.FINER, "processing subscribe request for subscriptionConfig={0}", filter);
if (dispatcher.subscribe(filter)) {
EventHistoryStore.onChannelSubscribe(filter.getChannelName());
}
Expand All @@ -159,8 +158,8 @@ public static class SubscriptionConfig {
private String batchId;
private String dispatcherId;
private HttpSession session;
private List<EventFilter> subscribeSet = Collections.emptyList();
private List<EventFilter> unsubscribeSet = Collections.emptyList();
private List<SubscriptionConfigEventFilter> subscribeSet = Collections.emptyList();
private List<SubscriptionConfigEventFilter> unsubscribeSet = Collections.emptyList();
private boolean unsubscribeAll = false;

public String getBatchId() {
Expand Down Expand Up @@ -192,15 +191,15 @@ public static SubscriptionConfig fromRequest(HttpServletRequest request) throws
return config;
}

private static List<EventFilter> extractFilterSet(JSONObject payload, String key) {
private static List<SubscriptionConfigEventFilter> extractFilterSet(JSONObject payload, String key) {
JSONArray jsonObjs = payload.optJSONArray(key);

if (jsonObjs != null && !jsonObjs.isEmpty()) {
List<EventFilter> filterSet = new ArrayList<>();
List<SubscriptionConfigEventFilter> filterSet = new ArrayList<>();
for (int i = 0; i < jsonObjs.size(); i++) {
try {
JSONObject jsonObj = jsonObjs.getJSONObject(i);
EventFilter filter = (EventFilter) jsonObj.toBean(EventFilter.class);
SubscriptionConfigEventFilter filter = (SubscriptionConfigEventFilter) jsonObj.toBean(SubscriptionConfigEventFilter.class);
filterSet.add(filter);
} catch (JSONException e) {
LOGGER.log(Level.SEVERE, "Invalid SSE payload. Expecting an array of JSON Objects for property " + key, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@
*/
package org.jenkinsci.plugins.ssegateway;

import hudson.Functions;
import com.google.common.base.Predicate;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
Expand All @@ -42,19 +40,18 @@
* Internal utility methods.
* @author <a href="mailto:[email protected]">[email protected]</a>
*/
@Restricted(NoExternalUse.class)
public class Util {

private Util() {
}

public static JSONObject readJSONPayload(HttpServletRequest request) throws IOException {
String characterEncoding = request.getCharacterEncoding();

if (characterEncoding == null) {
characterEncoding = "UTF-8";
}

Reader streamReader = new InputStreamReader(request.getInputStream(), characterEncoding);
try {
String payloadAsString = IOUtils.toString(streamReader);
Expand All @@ -65,25 +62,25 @@ public static JSONObject readJSONPayload(HttpServletRequest request) throws IOEx
}

private static Boolean isTestEnv = null;
public static boolean isTestEnv() {
public static boolean isTestEnv(final Predicate<Void> testEnvCheck) {
if (isTestEnv != null) {
return isTestEnv;
}
if (Functions.getIsUnitTest()) {

if (testEnvCheck != null && testEnvCheck.apply(null)) {
isTestEnv = true;
} else if (new File("./target/.jenkins_test").exists()) {
isTestEnv = true;
} else if (new File("./target/classes/" + Util.class.getName().replace(".", "/") + ".class").exists()) {
isTestEnv = true;
}

// If there's none of the markers, then we're not
// in a test env.
if (isTestEnv == null){
isTestEnv = false;
}

return isTestEnv;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package org.jenkinsci.plugins.ssegateway.sse;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.acegisecurity.Authentication;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
Expand Down Expand Up @@ -54,6 +55,10 @@ class AsynchEventDispatcher extends EventDispatcher {
@SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "Doesn't make sense to persist it")
private transient final Lock asyncContextLock = new ReentrantLock();

public AsynchEventDispatcher(final Authentication authentication) {
super(authentication);
}

@Override
public void start(HttpServletRequest request, HttpServletResponse response) {
final AsynchEventDispatcher dispatcher = this;
Expand Down
Loading