Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWinston Prakash2011-09-16 00:33:31 +0000
committerWinston Prakash2011-09-16 00:33:31 +0000
commitc74a6240b309125e185126ec4214a748b73eb1a9 (patch)
tree931dd006e49f6cb506257e0e0cbc0eb399153526 /hudson-service
downloadorg.eclipse.hudson.core-c74a6240b309125e185126ec4214a748b73eb1a9.tar.gz
org.eclipse.hudson.core-c74a6240b309125e185126ec4214a748b73eb1a9.tar.xz
org.eclipse.hudson.core-c74a6240b309125e185126ec4214a748b73eb1a9.zip
Initial commit of hudson-core files which are approved via CQ 5461
Diffstat (limited to 'hudson-service')
-rw-r--r--hudson-service/pom.xml59
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/events/EventConsumer.java30
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/events/EventPublisher.java35
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/events/internal/DiagnosticEventConsumer.java43
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/events/internal/EventPublisherImpl.java79
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyDetector.java120
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyDetectorStarter.java58
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyEvent.java39
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/BuildNotFoundException.java36
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/BuildService.java138
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/DependencyGraphService.java40
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/DescriptorService.java44
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/NodeNotFoundException.java32
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/NodeService.java87
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/NotFoundException.java51
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/ProjectNotFoundException.java39
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/ProjectService.java168
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/QueueService.java40
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/ScriptService.java38
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/SecurityService.java85
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/ServiceRuntimeException.java43
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/SystemIntegrityViolationException.java43
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/SystemService.java98
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/BuildServiceImpl.java133
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/DependencyGraphServiceImpl.java61
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/DescriptorServiceImpl.java71
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/DummyStaplerRequest.java385
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/DummyStaplerResponse.java209
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/NodeServiceImpl.java111
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/ProjectServiceImpl.java189
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/QueueServiceImpl.java53
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/ScriptServiceImpl.java63
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/SecurityServiceImpl.java119
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/ServicePreconditions.java163
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/ServiceSupport.java58
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/SystemServiceImpl.java233
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/internal/package-info.java17
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/service/package-info.java17
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletContainer.java37
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletContainerAware.java31
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletRegistration.java124
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletContainerImpl.java131
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletContainerStarter.java62
-rw-r--r--hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletRegistrationFilterAdapter.java175
-rw-r--r--hudson-service/src/test/java/org/eclipse/hudson/service/internal/BuildServiceImplTest.java394
-rw-r--r--hudson-service/src/test/java/org/eclipse/hudson/service/internal/DependencyGraphServiceImplTest.java57
-rw-r--r--hudson-service/src/test/java/org/eclipse/hudson/service/internal/DescriptorServiceImplTest.java65
-rw-r--r--hudson-service/src/test/java/org/eclipse/hudson/service/internal/NodeServiceImplTest.java198
-rw-r--r--hudson-service/src/test/java/org/eclipse/hudson/service/internal/ProjectServiceImplTest.java370
-rw-r--r--hudson-service/src/test/java/org/eclipse/hudson/service/internal/QueueServiceImplTest.java72
-rw-r--r--hudson-service/src/test/java/org/eclipse/hudson/service/internal/ScriptServiceImplTest.java82
-rw-r--r--hudson-service/src/test/java/org/eclipse/hudson/service/internal/SecurityServiceImplTest.java89
-rw-r--r--hudson-service/src/test/java/org/eclipse/hudson/service/internal/SystemServiceImplTest.java103
53 files changed, 5317 insertions, 0 deletions
diff --git a/hudson-service/pom.xml b/hudson-service/pom.xml
new file mode 100644
index 00000000..c814fe79
--- /dev/null
+++ b/hudson-service/pom.xml
@@ -0,0 +1,59 @@
+<!-- **************************************************************************
+#
+# Copyright (c) 2010-2011 Sonatype, Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#
+#
+#
+#
+#************************************************************************** -->
+
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.eclipse.hudson.main</groupId>
+ <artifactId>hudson</artifactId>
+ <version>2.1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>hudson-service</artifactId>
+ <name>Hudson :: Service</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.hudson.main</groupId>
+ <artifactId>hudson-plugin-utils</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.hudson.main</groupId>
+ <artifactId>hudson-test-utils</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>aspectj-maven-plugin</artifactId>
+ <configuration>
+ <aspectLibraries>
+ <aspectLibrary>
+ <groupId>org.eclipse.hudson.main</groupId>
+ <artifactId>hudson-inject</artifactId>
+ </aspectLibrary>
+ </aspectLibraries>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/events/EventConsumer.java b/hudson-service/src/main/java/org/eclipse/hudson/events/EventConsumer.java
new file mode 100644
index 00000000..b30f0742
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/events/EventConsumer.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.events;
+
+import java.util.EventObject;
+
+/**
+ * Provides support for listening to generic events.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+public interface EventConsumer
+{
+ void consume(EventObject event) throws Exception;
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/events/EventPublisher.java b/hudson-service/src/main/java/org/eclipse/hudson/events/EventPublisher.java
new file mode 100644
index 00000000..9ed552a4
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/events/EventPublisher.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.events;
+
+import com.google.inject.ImplementedBy;
+
+import org.eclipse.hudson.events.internal.EventPublisherImpl;
+
+import java.util.EventObject;
+
+/**
+ * Publishes events.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@ImplementedBy(EventPublisherImpl.class)
+public interface EventPublisher
+{
+ void publish(EventObject event);
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/events/internal/DiagnosticEventConsumer.java b/hudson-service/src/main/java/org/eclipse/hudson/events/internal/DiagnosticEventConsumer.java
new file mode 100644
index 00000000..23a33541
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/events/internal/DiagnosticEventConsumer.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.events.internal;
+
+import org.eclipse.hudson.events.EventConsumer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.util.EventObject;
+
+/**
+ * Adds diagnostic information when an event was published.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class DiagnosticEventConsumer
+ implements EventConsumer
+{
+ private static final Logger log = LoggerFactory.getLogger(DiagnosticEventConsumer.class);
+
+ public void consume(final EventObject event) throws Exception {
+ log.trace("Event published: {}", event);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/events/internal/EventPublisherImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/events/internal/EventPublisherImpl.java
new file mode 100644
index 00000000..70021b61
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/events/internal/EventPublisherImpl.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.events.internal;
+
+import org.eclipse.hudson.events.EventConsumer;
+import org.eclipse.hudson.events.EventPublisher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.util.EventObject;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default {@link EventPublisher} implementation.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class EventPublisherImpl
+ implements EventPublisher
+{
+ private static final Logger log = LoggerFactory.getLogger(EventPublisherImpl.class);
+
+ private final List<EventConsumer> consumers;
+
+ @Inject
+ public EventPublisherImpl(final List<EventConsumer> consumers) {
+ this.consumers = checkNotNull(consumers);
+ }
+
+ private EventConsumer[] getConsumers() {
+ return consumers.toArray(new EventConsumer[consumers.size()]);
+ }
+
+ public void publish(final EventObject event) {
+ checkNotNull(event);
+
+ log.trace("Publishing event: {}", event);
+
+ final ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+ for (EventConsumer target : getConsumers()) {
+ log.trace("Firing event ({}) to consumer: {}", event, target);
+
+ Thread.currentThread().setContextClassLoader(target.getClass().getClassLoader());
+
+ try {
+ target.consume(event);
+ }
+ catch (Exception e) {
+ log.error("Consumer raised an exception", e);
+ }
+ finally {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyDetector.java b/hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyDetector.java
new file mode 100644
index 00000000..73cbb7f2
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyDetector.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.events.ready;
+
+import org.eclipse.hudson.events.EventPublisher;
+
+import hudson.init.InitMilestone;
+import hudson.model.Hudson;
+import hudson.security.HudsonFilter;
+import hudson.stapler.WebAppController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import javax.servlet.Filter;
+import java.lang.reflect.Field;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Detects when the system is ready for work and publishes a {@link ReadyEvent}.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class ReadyDetector
+ extends Thread
+{
+ private static final Logger log = LoggerFactory.getLogger(ReadyDetector.class);
+
+ private final EventPublisher publisher;
+
+ private final Hudson hudson;
+
+ private final WebAppController controller;
+
+ @Inject
+ public ReadyDetector(final EventPublisher publisher, final Hudson hudson) {
+ this.publisher = checkNotNull(publisher);
+ this.hudson = checkNotNull(hudson);
+ this.controller = WebAppController.get();
+ setDaemon(true);
+ }
+
+ public void run() {
+ while (true) {
+ if (isReady()) {
+ publisher.publish(new ReadyEvent(hudson));
+ break;
+ }
+ else {
+ try {
+ Thread.sleep(500);
+ }
+ catch (InterruptedException e) {
+ log.warn("Interrupted while waiting for initialization; ignoring", e);
+ }
+ }
+ }
+ }
+
+ private boolean isReady() {
+ // Hudson does not give us a nice InitMilestone when its really ready,
+ // but if its not yet COMPLETED, don't bother trying to figure out more
+ if (hudson.getInitLevel() != InitMilestone.COMPLETED) {
+ return false;
+ }
+
+ HudsonFilter filter = HudsonFilter.get(hudson.servletContext);
+ if (filter == null) {
+ return false;
+ }
+
+ // Need to get access to the filter's filter field to see if its actually initialized or not
+ // it does not expose it directly, so we have to use reflection to force access
+
+ Filter delegate = getDelegate(filter);
+ if (delegate == null) {
+ return false;
+ }
+
+ // At this point we _should_ be ready, see if the app root object is installed... fingers crossed!
+ try {
+ Object app = controller.current(); // FIXME: This may actually be the only check needed?
+ return app instanceof hudson.model.Hudson;
+ } catch (IllegalStateException e) {
+ return false; // context not yet available
+ }
+ }
+
+ private Filter getDelegate(final HudsonFilter filter) {
+ assert filter != null;
+ try {
+ Field field = filter.getClass().getDeclaredField("filter");
+ field.setAccessible(true);
+ return (Filter) field.get(filter);
+ }
+ catch (Exception e) {
+ throw new Error("Failed to access HudsonFilter.filter delegate", e);
+ }
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyDetectorStarter.java b/hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyDetectorStarter.java
new file mode 100644
index 00000000..52da265b
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyDetectorStarter.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.events.ready;
+
+import org.eclipse.hudson.inject.Priority;
+
+import hudson.model.listeners.ItemListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Starts the {@link ReadyDetector}.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+@Priority(Integer.MIN_VALUE) // run last
+public class ReadyDetectorStarter
+ extends ItemListener
+{
+ private static final Logger log = LoggerFactory.getLogger(ReadyDetectorStarter.class);
+
+ // FIXME: Use of ItemListener for server life-cycle bits is a work around
+ private final ReadyDetector detector;
+
+ @Inject
+ public ReadyDetectorStarter(final ReadyDetector detector) {
+ this.detector = checkNotNull(detector);
+ }
+
+ @Override
+ public void onLoaded() {
+ log.debug("Starting ready detector");
+ detector.start();
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyEvent.java b/hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyEvent.java
new file mode 100644
index 00000000..b8085747
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/events/ready/ReadyEvent.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.events.ready;
+
+import hudson.model.Hudson;
+
+import java.util.EventObject;
+
+/**
+ * Event fired once the system is ready to be used.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+public class ReadyEvent
+ extends EventObject
+{
+ public ReadyEvent(final Hudson hudson) {
+ super(hudson);
+ }
+
+ public Hudson getHudson() {
+ return (Hudson)getSource();
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/BuildNotFoundException.java b/hudson-service/src/main/java/org/eclipse/hudson/service/BuildNotFoundException.java
new file mode 100644
index 00000000..3099c99a
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/BuildNotFoundException.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import hudson.model.AbstractBuild;
+
+/**
+ * Thrown when an {@link AbstractBuild} is needed to perform an operation, but
+ * could not be found.
+ *
+ * @author plynch
+ * @since 2.1.0
+ */
+public class BuildNotFoundException extends NotFoundException {
+ public BuildNotFoundException(String message) {
+ super(message);
+ }
+
+ public BuildNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/BuildService.java b/hudson-service/src/main/java/org/eclipse/hudson/service/BuildService.java
new file mode 100644
index 00000000..7a20eae9
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/BuildService.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import org.eclipse.hudson.service.internal.BuildServiceImpl;
+
+import com.google.inject.ImplementedBy;
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+
+/**
+ * Operations on {@link AbstractBuild} instances.
+ * <p>
+ * The intent of these operations is that the build is or already has executed
+ * and is available in the system.
+ * <p>
+ * Note: To schedule builds, see {@link ProjectService#scheduleBuild}. Since
+ * scheduled builds have not necessarily been executed, that is intentionally
+ * not an operation supported by this interface.
+ *
+ * @since 2.1.0
+ */
+@ImplementedBy(BuildServiceImpl.class)
+public interface BuildService {
+
+ // TODO void getConsole(AbstractProject<?, ?> project, int buildNumber);
+ // TODO void getConsoleContent(AbstractProject<?, ?> project, int
+ // buildNumber);
+ // TODO void getTestResults(AbstractProject<?, ?> project, int buildNumber);
+
+ /**
+ * Delete a build from a project by buildNumber
+ *
+ * <p>
+ * The current thread context must have {@link hudson.model.Run#DELETE}
+ * permission to perform this operation.
+ *
+ * @param project the project to the build belongs to
+ * @param buildNumber the buildNumber to operate on which must be
+ * {@literal >} zero
+ * @throws BuildNotFoundException if a build with the buildNumber could not
+ * be found
+ * @throws ServiceRuntimeException if an unexpected condition prevents build
+ * deletion
+ */
+ void deleteBuild(AbstractProject<?, ?> project, int buildNumber);
+
+ /**
+ * Keep or release a build.
+ *
+ * <p>
+ * The current thread context must have {@link hudson.model.Run#UPDATE}
+ * permission to perform this operation.
+ *
+ * @param project the project to the build belongs to
+ * @param buildNumber the buildNumber to operate on which must be
+ * {@literal >} zero
+ * @param release false to keep the build, true to release
+ * @throws BuildNotFoundException if a build with the buildNumber could not
+ * be found
+ * @throws ServiceRuntimeException if an unexpected condition prevents build
+ * keep or release
+ */
+ void keepBuild(AbstractProject<?, ?> project, int buildNumber, boolean release);
+
+ /**
+ * Stop or abort a build
+ *
+ * <p>
+ * The current thread context must have permission to abort the build.
+ *
+ * @param project the project to the build belongs to
+ * @param buildNumber the buildNumber to operate on which must be
+ * {@literal >} zero
+ * @throws BuildNotFoundException if a build with the buildNumber could not
+ * be found
+ * @throws ServiceRuntimeException if an unexpected condition prevents build
+ * stoppage
+ */
+ void stopBuild(AbstractProject<?, ?> project, int buildNumber);
+
+ /**
+ * Find a build in the project by buildNumber.
+ *
+ * @param project
+ * @param buildNumber
+ * @return the build instance or null if there is no such build
+ */
+ AbstractBuild findBuild(AbstractProject<?, ?> project, int buildNumber);
+
+ /**
+ * Find a build in the project by buildNumber.
+ *
+ * @param projectName the project name used to look up the build
+ * @param buildNumber the build number of the project build, which should be
+ * greater than 0
+ * @return the build instance or null if there is no such build
+ */
+ AbstractBuild findBuild(String projectName, int buildNumber);
+
+ /**
+ * Find a build in the project by buildNumber.
+ *
+ * @param project
+ * @param buildNumber
+ * @return the build instance, never null
+ * @throws BuildNotFoundException if the build could not be found in the
+ * project
+ */
+ AbstractBuild getBuild(AbstractProject<?, ?> project, int buildNumber);
+
+ /**
+ * Get a build in the project by buildNumber.
+ *
+ * @param projectName the project name used to look up the build
+ * @param buildNumber the build number of the project build, which should be
+ * greater than 0
+ * @return the build instance, never null
+ * @throws BuildNotFoundException if the build could not be found in the
+ * project
+ */
+ AbstractBuild getBuild(String projectName, int buildNumber);
+
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/DependencyGraphService.java b/hudson-service/src/main/java/org/eclipse/hudson/service/DependencyGraphService.java
new file mode 100644
index 00000000..524bbbaa
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/DependencyGraphService.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import org.eclipse.hudson.service.internal.DependencyGraphServiceImpl;
+
+import hudson.model.DependencyGraph;
+import hudson.model.TaskListener;
+import hudson.model.AbstractBuild;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * {@link DependencyGraph} related services.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@ImplementedBy(DependencyGraphServiceImpl.class)
+public interface DependencyGraphService {
+ DependencyGraph getGraph();
+
+ void rebuild();
+
+ void triggerDependents(AbstractBuild<?, ?> build, TaskListener listener);
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/DescriptorService.java b/hudson-service/src/main/java/org/eclipse/hudson/service/DescriptorService.java
new file mode 100644
index 00000000..0dae748d
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/DescriptorService.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import org.eclipse.hudson.service.internal.DescriptorServiceImpl;
+
+import com.google.inject.ImplementedBy;
+import hudson.DescriptorExtensionList;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+
+/**
+ * Service operations for {@link Descriptor} and {@link Describable}
+ *
+ * @since 2.1.0
+ */
+@ImplementedBy(DescriptorServiceImpl.class)
+public interface DescriptorService {
+
+ Descriptor getDescriptor(Class<? extends Describable> type);
+
+ Descriptor getDescriptor(String className);
+
+ <T extends Descriptor> T getDescriptorByType(Class<T> type);
+
+ <T extends Describable<T>, D extends Descriptor<T>> DescriptorExtensionList<T, D> getDescriptorList(Class<T> type);
+
+ Descriptor getDescriptorOrDie(Class<? extends Describable> type);
+
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/NodeNotFoundException.java b/hudson-service/src/main/java/org/eclipse/hudson/service/NodeNotFoundException.java
new file mode 100644
index 00000000..6fdc78f9
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/NodeNotFoundException.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+/**
+ * Thrown when a {@link hudson.model.Node} cannot be found in the system.
+ *
+ * @since 2.1.0
+ */
+public class NodeNotFoundException extends NotFoundException {
+ public NodeNotFoundException(String message) {
+ super(message);
+ }
+
+ public NodeNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/NodeService.java b/hudson-service/src/main/java/org/eclipse/hudson/service/NodeService.java
new file mode 100644
index 00000000..6d34428e
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/NodeService.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import hudson.model.Node;
+
+import java.util.List;
+
+import org.acegisecurity.AccessDeniedException;
+import org.eclipse.hudson.service.internal.NodeServiceImpl;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * {@link hudson.model.Node} related services.
+ *
+ * @since 2.1.0
+ */
+@ImplementedBy(NodeServiceImpl.class)
+public interface NodeService {
+ /**
+ * Get the node with the given name.
+ *
+ * @param nodeName the name of the node to get
+ * @return the node
+ * @throws NodeNotFoundException if node cannot be found
+ * @throws NullPointerException if nodeName is null
+ * @throws AccessDeniedException if context does not have
+ * {@link hudson.model.Item#READ} permission to access the master node
+ */
+ Node getNode(final String nodeName);
+
+ /**
+ * Find the node with the given name
+ *
+ * @param nodeName the name of the node to find
+ * @return the node if found, else null
+ * @throws NullPointerException if nodeName is null
+ * @throws AccessDeniedException if context does not have
+ * {@link hudson.model.Item#READ} permission to access the master node
+ */
+ Node findNode(final String nodeName);
+
+ /**
+ *
+ * @return the master Hudson node, if permissions
+ * {@link hudson.model.Item#READ}.
+ * @throws AccessDeniedException if context does not have
+ * {@link hudson.model.Item#READ} permission to access the master node
+ */
+ Node getMasterNode();
+
+ /**
+ * @return all {@link Node}s in the system, including the master, that have
+ * {@link hudson.model.Item#READ} permission.
+ */
+ List<Node> getAllNodes();
+
+ /**
+ * @returns all {@link Node}s in the system, excluding {@link hudson.model.Hudson}
+ * instance itself which represents the master, that have
+ * {@link hudson.model.Item#READ} permission.
+ */
+ List<Node> getNodes();
+
+ /**
+ * @return the current node, if the context has
+ * {@link hudson.model.Item#READ} permission
+ * @throws AccessDeniedException if context does not have
+ * {@link hudson.model.Item#READ} permission to access the master node
+ */
+ Node getCurrentNode();
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/NotFoundException.java b/hudson-service/src/main/java/org/eclipse/hudson/service/NotFoundException.java
new file mode 100644
index 00000000..f7c73c33
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/NotFoundException.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+/**
+ * Thrown when a system entity that is needed to perform a service level action
+ * cannot be found within the system.
+ * <p>
+ * An example of where this may be useful is when a source project identified by
+ * a project name may not exist any more, and therefore cannot be copied to a
+ * new project. Also, typically of the service APIs, all {@literal get*} methods
+ * may throw this when an entity is not found.
+ * <p>
+ * Extend this class with a specific implementation for each type of system
+ * entity that may not be found.
+ * <p>
+ * Service API users should do their best to verify an entity exists prior to
+ * performing an operation that may trigger this exception.
+ *
+ * @since 2.1.0
+ */
+public abstract class NotFoundException extends ServiceRuntimeException {
+ protected NotFoundException() {
+ }
+
+ protected NotFoundException(String message) {
+ super(message);
+ }
+
+ protected NotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ protected NotFoundException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/ProjectNotFoundException.java b/hudson-service/src/main/java/org/eclipse/hudson/service/ProjectNotFoundException.java
new file mode 100644
index 00000000..7d04aec3
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/ProjectNotFoundException.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import hudson.model.AbstractProject;
+
+/**
+ * Thrown when an {@link AbstractProject} is needed to perform an operation, but could not be located.
+ *
+ * @author plynch
+ * @since 2.1.0
+ */
+public class ProjectNotFoundException
+ extends NotFoundException
+{
+ public ProjectNotFoundException(String message)
+ {
+ super(message);
+ }
+
+ public ProjectNotFoundException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/ProjectService.java b/hudson-service/src/main/java/org/eclipse/hudson/service/ProjectService.java
new file mode 100644
index 00000000..a903edf3
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/ProjectService.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import com.google.inject.ImplementedBy;
+import hudson.model.AbstractProject;
+import hudson.model.Item;
+import hudson.model.TopLevelItem;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+import org.eclipse.hudson.service.internal.ProjectServiceImpl;
+
+/**
+ * Service API related to Projects and Job models, such as that by {@link AbstractProject}.
+ *
+ * @author plynch
+ * @since 2.1.0
+ */
+@ImplementedBy(ProjectServiceImpl.class)
+public interface ProjectService
+{
+ /**
+ * Get the project identified by a UUID.
+ *
+ * @param id UUID identifier of the project to get
+ * @return the project associated with the UUID
+ * @throws ProjectNotFoundException if a project cannot be found for the given UUID
+ */
+ AbstractProject<?, ?> getProject(final UUID id);
+
+ /**
+ * Get the project identified by a UUID.
+ *
+ * @param id UUID identifier of the project to get
+ * @return the project associated with the UUID or null if not found
+ */
+ AbstractProject<?, ?> findProject(final UUID id);
+
+ /**
+ * Get the project identified by a full project name.
+ *
+ * The current thread context must have {@link Item#READ} permission on the project in order to
+ * get it.
+ *
+ * @param projectFullName full project name as reported by {@link AbstractProject#getFullName}
+ * @return the project associated with projectFullName
+ * @throws ProjectNotFoundException if a project with the specified full name does not exist
+ */
+ AbstractProject<?, ?> getProjectByFullName(String projectFullName);
+
+ /**
+ * Find the project identified by a full project name.
+ *
+ * The current thread context must have {@link Item#READ} permission on the project in order to
+ * find it.
+ *
+ * @param projectFullName full project name as reported by {@link AbstractProject#getFullName}
+ * @return the project associated with projectFullName or null if not found
+ */
+ AbstractProject<?, ?> findProjectByFullName(String projectFullName);
+
+ /**
+ * Check if a project with given name as reported by {@link AbstractProject#getName}
+ * already exists in the system.
+ *
+ * In order to determine if a project exists, the current thread context must have {@link Item#READ}
+ * permission on it.
+ *
+ * @param projectName project name to lookup
+ * @return true if a project with the specified name is readable and exists in the system
+ */
+ boolean projectExists(String projectName);
+
+ /**
+ * Get a list of all projects of a specific {@literal type} in the system.
+ *
+ * <p>The current thread must have {@link Item#READ} permission
+ * on each project returned and {@link Item#READ} permission on the {@link hudson.model.Node}
+ * where the projects reside.
+ *
+ * @return a list of all projects implemented by type T
+ */
+ @SuppressWarnings("rawtypes")
+ List<AbstractProject> getAllProjects();
+
+ /**
+ * Copy a source project to a new project which will be named the
+ * value of {@literal targetProjectName}
+ *
+ * <p>The current thread must have {@link Item#EXTENDED_READ} permission
+ * on {@literal src} and {@link Item#CREATE} permission on the {@link hudson.model.Node}
+ * where the project will be copied too, in order for the operation to succeed.
+ *
+ * @param <T> the type of new target project
+ * @param src source project to copy from
+ * @param targetProjectName the name to create the new project with
+ * @return the newly created target project
+ * @throws SystemIntegrityViolationException if a project already exists with the targetProjectName
+ * @throws ServiceRuntimeException if there was an unexpected problem creating the project
+ */
+ <T extends AbstractProject<?, ?>> T copyProject(T src, String targetProjectName);
+
+ /**
+ * Create a new project in the system from XML.
+ *
+ * <p>The current thread must have {@link hudson.model.Item#CREATE} permission on the
+ * {@link hudson.model.Node} where the project will created, in order for the operation to
+ * succeed.
+ *
+ * <p>Buffering or closing of the InputStream should be done outside of this method.
+ *
+ * @param projectName the name of the new project.
+ * @param xml XML document describing the new project
+ * @return a {@link hudson.model.TopLevelItem} representing the newly created project
+ * @throws SystemIntegrityViolationException if a project already exists with projectName
+ * @throws ServiceRuntimeException if there was an unexpected problem creating the project
+ */
+ TopLevelItem createProjectFromXML(String projectName, InputStream xml);
+
+ /**
+ * Find and return a project with the given name ( as defined by {@link AbstractProject#getName} )
+ * , or return {@literal null} if one cannot be found.
+ *
+ * <p>The current thread context must have {@link Item#READ} permission on the project in order to find it.
+ *
+ * @param projectName
+ * @return a project for the specified name if it is readable and exists, otherwise null
+ */
+ AbstractProject<?,?> findProject(String projectName);
+
+ /**
+ * Tries to find a project with the given name as defined by {@link AbstractProject#getName}.
+ *
+ * <p>The current thread context must have {@link Item#READ} permission on the project in order to find it.
+ *
+ * @param projectName the project name for the project
+ * @return an AbstractProject for the specified name if one exists, never null
+ * @throws ProjectNotFoundException if the project cannot be found
+ */
+ AbstractProject<?,?> getProject(String projectName);
+
+ /**
+ * Return a collection of all project names in the system.
+ *
+ * <p>{@link Item#READ} access is required to return a job name in the collection.
+ *
+ * @return a collection, never null, of all project names in the system
+ */
+ Collection<String> getProjectNames();
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/QueueService.java b/hudson-service/src/main/java/org/eclipse/hudson/service/QueueService.java
new file mode 100644
index 00000000..e05ab031
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/QueueService.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import com.google.inject.ImplementedBy;
+import hudson.model.Hudson;
+import hudson.model.Queue;
+import org.acegisecurity.AccessDeniedException;
+import org.eclipse.hudson.service.internal.QueueServiceImpl;
+
+/**
+ * Default implementation of {@link QueueService}
+ *
+ * @since 2.1.0
+ */
+@ImplementedBy(QueueServiceImpl.class)
+public interface QueueService
+{
+ /**
+ * Get the queue.
+ *
+ * @return the master {@link Queue}
+ * @throws AccessDeniedException if current context does not have {@link Hudson#ADMINISTER} permission.
+ */
+ Queue getQueue();
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/ScriptService.java b/hudson-service/src/main/java/org/eclipse/hudson/service/ScriptService.java
new file mode 100644
index 00000000..65d7f643
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/ScriptService.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import org.eclipse.hudson.service.internal.ScriptServiceImpl;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * Provides script execution services.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@ImplementedBy(ScriptServiceImpl.class)
+public interface ScriptService
+{
+ /**
+ * Execute a script on the master.
+ */
+ String execute(String script) throws Exception;
+
+ // TODO: Add execution for a specific node
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/SecurityService.java b/hudson-service/src/main/java/org/eclipse/hudson/service/SecurityService.java
new file mode 100644
index 00000000..92e0bb05
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/SecurityService.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import hudson.model.User;
+import hudson.security.AccessControlled;
+import hudson.security.Permission;
+
+import java.util.concurrent.Callable;
+
+import org.acegisecurity.AccessDeniedException;
+import org.acegisecurity.Authentication;
+import org.eclipse.hudson.service.internal.SecurityServiceImpl;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * Security services.
+ *
+ * @since 2.1.0
+ */
+@ImplementedBy(SecurityServiceImpl.class)
+public interface SecurityService {
+ /**
+ * Check a Permission against {@link hudson.model.Hudson} for the current
+ * user.
+ *
+ * @see hudson.model.Hudson#checkPermission(Permission)
+ * @param permission the permission to check
+ * @throws AccessDeniedException if access for the given permission is
+ * denied
+ */
+ void checkPermission(Permission permission);
+
+ /**
+ * Check a Permission against an {@link AccessControlled} object in the current
+ * security context.
+ * <p>
+ * Recommended to use this instead of checking permission on the object
+ * directly. Consider this method a funnel for access security.
+ *
+ * @param controlled the instance under control
+ * @param permission the permission to check on the access controlled object
+ */
+ void checkPermission(AccessControlled controlled, Permission permission);
+
+ /**
+ * Check if a an {@link AccessControlled} instance will allow the current
+ * security context the specified {@link Permission}.
+ * <p>
+ * Recommended to use this instead of checking has permission on the object
+ * directly. Consider this method a funnel for access security.
+ *
+ * @param controlled the instance under control
+ * @param permission the permission to check on the access controlled object
+ * @return true if current security context has the specified permission
+ */
+ boolean hasPermission(AccessControlled controlled, Permission permission);
+
+ User getCurrentUser();
+
+ User getUser(String id);
+
+ User getUnknownUser();
+
+ void runAs(Authentication auth, Runnable task);
+
+ <T> T callAs(Authentication auth, Callable<T> task) throws Exception;
+
+ <T> T callAs2(Authentication auth, Callable<T> task);
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/ServiceRuntimeException.java b/hudson-service/src/main/java/org/eclipse/hudson/service/ServiceRuntimeException.java
new file mode 100644
index 00000000..8311e5be
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/ServiceRuntimeException.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+/**
+ * Base class of all service layer {@link RuntimeException}s.
+ * <p>
+ * Generally the service layer API should only throw unchecked exceptions that extend this base class.
+ *
+ * @since 2.1.0
+ */
+public class ServiceRuntimeException
+ extends RuntimeException
+{
+ public ServiceRuntimeException() {
+ }
+
+ public ServiceRuntimeException(String message) {
+ super(message);
+ }
+
+ public ServiceRuntimeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ServiceRuntimeException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/SystemIntegrityViolationException.java b/hudson-service/src/main/java/org/eclipse/hudson/service/SystemIntegrityViolationException.java
new file mode 100644
index 00000000..b1e8c79c
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/SystemIntegrityViolationException.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+/**
+ * Thrown when the integrity of the system would be compromised if
+ * a requested operation was allowed to proceed.
+ *
+ * @author plynch
+ * @since 2.1.0
+ */
+public class SystemIntegrityViolationException
+ extends ServiceRuntimeException
+{
+ public SystemIntegrityViolationException() {
+ }
+
+ public SystemIntegrityViolationException(String message) {
+ super(message);
+ }
+
+ public SystemIntegrityViolationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SystemIntegrityViolationException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/SystemService.java b/hudson-service/src/main/java/org/eclipse/hudson/service/SystemService.java
new file mode 100644
index 00000000..4a29a3b5
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/SystemService.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
+
+import com.google.inject.ImplementedBy;
+import hudson.XmlFile;
+import hudson.init.InitMilestone;
+import hudson.model.Hudson;
+
+import java.io.File;
+
+import org.eclipse.hudson.service.internal.SystemServiceImpl;
+
+/**
+ * General system access.
+ *
+ * <p>All operations involving restarting, quiet down, or reloading the system require {@link Hudson#ADMINISTER} permission.
+ *
+ * @author plynch
+ * @since 2.1.0
+ */
+@ImplementedBy(SystemServiceImpl.class)
+public interface SystemService
+{
+ String DEFAULT_URL = "http://localhost:8082";
+
+ /**
+ * The installation location.
+ *
+ * @throws IllegalStateException when the installation directory cannot be reliably determined
+ */
+ File getInstallationDirectory();
+
+ /**
+ * Get the working directory of the server
+ */
+ // TODO: as of 1.1 this is the "state" directory, maybe rename
+ File getWorkingDirectory();
+
+ /**
+ * The default location where the server will store log files.
+ *
+ * @throws IllegalStateException if the log directory cannot be reliably determined
+ */
+ File getLogDirectory();
+
+ /**
+ * Return the {@link XmlFile} representation of the system config file
+ *
+ * The current thread requires {@link Hudson#ADMINISTER} permission to get the file.
+ *
+ * @return XmlFile representation of the system config file,
+ * @throws ServiceRuntimeException if there is an unexpected problem accessing the config file.
+ */
+ XmlFile getConfigFile();
+
+ String getUrl();
+
+ String getVersion();
+
+ InitMilestone getInitLevel();
+
+ String getSystemMessage();
+
+ // FIXME: Drop do* prefix on these methods. do* is specific to stapler and should be dropped from the service API
+
+ boolean isQuietingDown();
+
+ void doQuietDown(boolean toggle);
+
+ void doQuietDown();
+
+ void doCancelQuietDown();
+
+ void doReload();
+
+ boolean isTerminating();
+
+ void doRestart();
+
+ void doRestart(boolean safe);
+
+ void doRestartSafely();
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/BuildServiceImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/BuildServiceImpl.java
new file mode 100644
index 00000000..c52cbd5b
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/BuildServiceImpl.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.eclipse.hudson.service.internal.ServicePreconditions.*;
+import static org.eclipse.hudson.utils.common.Varargs.$;
+import hudson.model.Item;
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+import hudson.model.Run;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import javax.servlet.ServletException;
+
+import org.eclipse.hudson.service.BuildNotFoundException;
+import org.eclipse.hudson.service.BuildService;
+import org.eclipse.hudson.service.ProjectService;
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.ServiceRuntimeException;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Default implementation of {@link BuildService}.
+ *
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class BuildServiceImpl extends ServiceSupport implements BuildService {
+ private final ProjectService projects;
+ private final SecurityService security;
+
+ @Inject
+ BuildServiceImpl(final ProjectService projects, SecurityService security) {
+ this.projects = Preconditions.checkNotNull(projects);
+ this.security = Preconditions.checkNotNull(security);
+ }
+
+ public void deleteBuild(final AbstractProject<?, ?> project, final int buildNumber) {
+ AbstractBuild<?, ?> build = getBuild(project, buildNumber);
+ this.security.checkPermission(build, Run.DELETE);
+ log.debug("Deleting build: {} #{}", project.getName(), buildNumber);
+ try {
+ build.delete();
+ } catch (IOException e) {
+ throw new ServiceRuntimeException("Delete failed for build " + project.getName() + " #" + buildNumber, e);
+ }
+ }
+
+ public void keepBuild(final AbstractProject<?, ?> project, final int buildNumber, final boolean release) {
+ AbstractBuild<?, ?> build = getBuild(project, buildNumber);
+ this.security.checkPermission(build, Run.UPDATE);
+ log.debug("{} build: {} #{}", $(release ? "Releasing" : "Keeping", project.getName(), buildNumber));
+
+ try {
+ build.keepLog(!release);
+ } catch (IOException e) {
+ throw new ServiceRuntimeException((release ? "Releasing failed for build #" : "Keeping failed for build ")
+ + project.getName() + " #" + buildNumber);
+ }
+ }
+
+ public AbstractBuild<?, ?> getBuild(final String projectName, final int buildNumber) {
+ checkProjectName(projectName);
+ checkBuildNumber(buildNumber);
+
+ AbstractProject<?, ?> project = projects.getProject(projectName);
+ return getBuild(project, buildNumber);
+ }
+
+ public AbstractBuild<?, ?> getBuild(final AbstractProject<?,?> project, final int buildNumber)
+ throws BuildNotFoundException {
+ AbstractBuild<?, ?> build = findBuild(project, buildNumber);
+ if (build == null) {
+ throw new BuildNotFoundException("Build " + project.getName() + " #" + buildNumber + " could not be found.");
+ }
+ return build;
+ }
+
+ public AbstractBuild<?, ?> findBuild(final String projectName, final int buildNumber) {
+ checkProjectName(projectName);
+ checkBuildNumber(buildNumber);
+
+ AbstractProject<?, ?> project = projects.findProject(projectName);
+ return project != null ? findBuild(project, buildNumber) : null;
+ }
+
+ public AbstractBuild<?,?> findBuild(final AbstractProject<?, ?> project, final int buildNumber) {
+ checkNotNull(project, "project");
+ checkBuildNumber(buildNumber);
+
+ AbstractBuild<?,?> build = project.getBuildByNumber(buildNumber);
+
+ if (build != null) {
+ this.security.checkPermission(build, Item.READ);
+ }
+
+ return build;
+ }
+
+ public void stopBuild(final AbstractProject<?, ?> project, final int buildNumber){
+ AbstractBuild<?, ?> build = getBuild(project, buildNumber);
+ log.debug("Stopping build: {} #{}", project.getName(), buildNumber);
+ try {
+ // Security: doStop eventually checks to see if the task owner has permission to abort the build
+ build.doStop(DummyStaplerRequest.INSTANCE, DummyStaplerResponse.INSTANCE);
+ } catch (IOException e) {
+ throw new ServiceRuntimeException("Stop failed for " + project.getName() + " #" + buildNumber, e);
+ } catch (ServletException e) {
+ throw new ServiceRuntimeException("Stop failed for " + project.getName() + " #" + buildNumber, e);
+ }
+ }
+
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DependencyGraphServiceImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DependencyGraphServiceImpl.java
new file mode 100644
index 00000000..adac59e1
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DependencyGraphServiceImpl.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.eclipse.hudson.service.internal.ServicePreconditions.*;
+import hudson.model.BuildListener;
+import hudson.model.DependencyGraph;
+import hudson.model.TaskListener;
+import hudson.model.AbstractBuild;
+import hudson.tasks.BuildTrigger;
+
+import javax.inject.Singleton;
+
+import org.eclipse.hudson.service.DependencyGraphService;
+
+
+/**
+ * Default implementation of {@link DependencyGraphService}.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@Singleton
+public class DependencyGraphServiceImpl extends ServiceSupport implements DependencyGraphService {
+
+ public DependencyGraph getGraph() {
+ return getHudson().getDependencyGraph();
+ }
+
+ public void rebuild() {
+ log.debug("Rebuilding dependency graph");
+ getHudson().rebuildDependencyGraph();
+ }
+
+ public void triggerDependents(final AbstractBuild<?, ?> build, final TaskListener listener) {
+ checkNotNull(build, "build");
+ checkNotNull(listener, "listener");
+
+ if (log.isDebugEnabled()) {
+ log.debug("Maybe triggering dependents of build: {}", build.getFullDisplayName());
+ }
+
+ // FIXME: In a more perfect world, the BuildTrigger would use this
+ // service, instead of us calling it directly here
+ BuildTrigger.execute(build, (BuildListener) listener);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DescriptorServiceImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DescriptorServiceImpl.java
new file mode 100644
index 00000000..a5a216bb
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DescriptorServiceImpl.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.eclipse.hudson.service.internal.ServicePreconditions.*;
+import hudson.DescriptorExtensionList;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.eclipse.hudson.service.DescriptorService;
+
+/**
+ * Default implementation of {@link DescriptorService}
+ *
+ * <b>Note:</b> Should not normally access this publicly since no security
+ * checks are present.
+ *
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class DescriptorServiceImpl extends ServiceSupport implements DescriptorService {
+ @Inject
+ DescriptorServiceImpl() {
+ }
+
+ public Descriptor getDescriptor(final String className) {
+ checkNotNull(className, "class name");
+ return getHudson().getDescriptor(className);
+ }
+
+ public Descriptor getDescriptor(Class<? extends Describable> type) {
+ checkNotNull(type, "type");
+ return getHudson().getDescriptor(type);
+ }
+
+ public Descriptor getDescriptorOrDie(Class<? extends Describable> type) {
+ checkNotNull(type, "type");
+ return getHudson().getDescriptorOrDie(type);
+ }
+
+ public <T extends Descriptor> T getDescriptorByType(Class<T> type) {
+ checkNotNull(type, "type");
+ return getHudson().getDescriptorByType(type);
+ }
+
+ public <T extends Describable<T>, D extends Descriptor<T>> DescriptorExtensionList<T, D> getDescriptorList(
+ Class<T> type) {
+ checkNotNull(type, "type");
+ return getHudson().getDescriptorList(type);
+ }
+
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DummyStaplerRequest.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DummyStaplerRequest.java
new file mode 100644
index 00000000..92a9a3b3
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DummyStaplerRequest.java
@@ -0,0 +1,385 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import net.sf.json.JSONObject;
+import org.apache.commons.fileupload.FileItem;
+import org.kohsuke.stapler.Ancestor;
+import org.kohsuke.stapler.Stapler;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpSession;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Type;
+import java.security.Principal;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * A dummy {@link StaplerRequest}.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+public class DummyStaplerRequest
+ implements StaplerRequest
+{
+ public static final DummyStaplerRequest INSTANCE = new DummyStaplerRequest();
+
+ public Stapler getStapler() {
+ return null;
+ }
+
+ public String getRestOfPath() {
+ return null;
+ }
+
+ public String getOriginalRestOfPath() {
+ return null;
+ }
+
+ public ServletContext getServletContext() {
+ return null;
+ }
+
+ public RequestDispatcher getView(Object it, String viewName) throws IOException {
+ return null;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public RequestDispatcher getView(Class clazz, String viewName) throws IOException {
+ return null;
+ }
+
+ public String getRootPath() {
+ return null;
+ }
+
+ public String getReferer() {
+ return null;
+ }
+
+ public List<Ancestor> getAncestors() {
+ return null;
+ }
+
+ public Ancestor findAncestor(@SuppressWarnings("rawtypes") Class type) {
+ return null;
+ }
+
+ public <T> T findAncestorObject(Class<T> type) {
+ return null;
+ }
+
+ public Ancestor findAncestor(Object o) {
+ return null;
+ }
+
+ public boolean hasParameter(String name) {
+ return false;
+ }
+
+ public String getOriginalRequestURI() {
+ return null;
+ }
+
+ public boolean checkIfModified(long timestampOfResource, StaplerResponse rsp) {
+ return false;
+ }
+
+ public boolean checkIfModified(Date timestampOfResource, StaplerResponse rsp) {
+ return false;
+ }
+
+ public boolean checkIfModified(Calendar timestampOfResource, StaplerResponse rsp) {
+ return false;
+ }
+
+ public boolean checkIfModified(long timestampOfResource, StaplerResponse rsp, long expiration) {
+ return false;
+ }
+
+ public void bindParameters(Object bean) {
+ }
+
+ public void bindParameters(Object bean, String prefix) {
+ }
+
+ public <T> List<T> bindParametersToList(Class<T> type, String prefix) {
+ return null;
+ }
+
+ public <T> T bindParameters(Class<T> type, String prefix) {
+ return null;
+ }
+
+ public <T> T bindParameters(Class<T> type, String prefix, int index) {
+ return null;
+ }
+
+ public <T> T bindJSON(Class<T> type, JSONObject src) {
+ return null;
+ }
+
+ public <T> T bindJSON(Type genericType, Class<T> erasure, Object json) {
+ return null;
+ }
+
+ public void bindJSON(Object bean, JSONObject src) {
+ }
+
+ public <T> List<T> bindJSONToList(Class<T> type, Object src) {
+ return null;
+ }
+
+ public JSONObject getSubmittedForm() throws ServletException {
+ return null;
+ }
+
+ public FileItem getFileItem(String name) throws ServletException, IOException {
+ return null;
+ }
+
+ public boolean isJavaScriptProxyCall() {
+ return false;
+ }
+
+ public String getAuthType() {
+ return null;
+ }
+
+ public Cookie[] getCookies() {
+ return new Cookie[0];
+ }
+
+ public long getDateHeader(String name) {
+ return 0;
+ }
+
+ public String getHeader(String name) {
+ return null;
+ }
+
+ public Enumeration<?> getHeaders(String name) {
+ return null;
+ }
+
+ public Enumeration<?> getHeaderNames() {
+ return null;
+ }
+
+ public int getIntHeader(String name) {
+ return 0;
+ }
+
+ public String getMethod() {
+ return null;
+ }
+
+ public String getPathInfo() {
+ return null;
+ }
+
+ public String getPathTranslated() {
+ return null;
+ }
+
+ public String getContextPath() {
+ return null;
+ }
+
+ public String getQueryString() {
+ return null;
+ }
+
+ public String getRemoteUser() {
+ return null;
+ }
+
+ public boolean isUserInRole(String role) {
+ return false;
+ }
+
+ public Principal getUserPrincipal() {
+ return null;
+ }
+
+ public String getRequestedSessionId() {
+ return null;
+ }
+
+ public String getRequestURI() {
+ return null;
+ }
+
+ public StringBuffer getRequestURL() {
+ return null;
+ }
+
+ public String getServletPath() {
+ return null;
+ }
+
+ public HttpSession getSession(boolean create) {
+ return null;
+ }
+
+ public HttpSession getSession() {
+ return null;
+ }
+
+ public boolean isRequestedSessionIdValid() {
+ return false;
+ }
+
+ public boolean isRequestedSessionIdFromCookie() {
+ return false;
+ }
+
+ public boolean isRequestedSessionIdFromURL() {
+ return false;
+ }
+
+ public boolean isRequestedSessionIdFromUrl() {
+ return false;
+ }
+
+ public Object getAttribute(String name) {
+ return null;
+ }
+
+ public Enumeration<?> getAttributeNames() {
+ return null;
+ }
+
+ public String getCharacterEncoding() {
+ return null;
+ }
+
+ public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
+ }
+
+ public int getContentLength() {
+ return 0;
+ }
+
+ public String getContentType() {
+ return null;
+ }
+
+ public ServletInputStream getInputStream() throws IOException {
+ return null;
+ }
+
+ public String getParameter(String name) {
+ return null;
+ }
+
+ public Enumeration<?> getParameterNames() {
+ return null;
+ }
+
+ public String[] getParameterValues(String name) {
+ return new String[0];
+ }
+
+ public Map<?, ?> getParameterMap() {
+ return null;
+ }
+
+ public String getProtocol() {
+ return null;
+ }
+
+ public String getScheme() {
+ return null;
+ }
+
+ public String getServerName() {
+ return null;
+ }
+
+ public int getServerPort() {
+ return 0;
+ }
+
+ public BufferedReader getReader() throws IOException {
+ return null;
+ }
+
+ public String getRemoteAddr() {
+ return null;
+ }
+
+ public String getRemoteHost() {
+ return null;
+ }
+
+ public void setAttribute(String name, Object o) {
+ }
+
+ public void removeAttribute(String name) {
+ }
+
+ public Locale getLocale() {
+ return null;
+ }
+
+ public Enumeration<?> getLocales() {
+ return null;
+ }
+
+ public boolean isSecure() {
+ return false;
+ }
+
+ public RequestDispatcher getRequestDispatcher(String path) {
+ return null;
+ }
+
+ public String getRealPath(String path) {
+ return null;
+ }
+
+ public int getRemotePort() {
+ return 0;
+ }
+
+ public String getLocalName() {
+ return null;
+ }
+
+ public String getLocalAddr() {
+ return null;
+ }
+
+ public int getLocalPort() {
+ return 0;
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DummyStaplerResponse.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DummyStaplerResponse.java
new file mode 100644
index 00000000..4821584f
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/DummyStaplerResponse.java
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.kohsuke.stapler.export.Flavor;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.net.URL;
+import java.util.Locale;
+
+/**
+ * A dummy {@link StaplerResponse}.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+public class DummyStaplerResponse
+ implements StaplerResponse
+{
+ public static final DummyStaplerResponse INSTANCE = new DummyStaplerResponse();
+
+ public void forward(Object it, String url, StaplerRequest request) throws ServletException, IOException {
+ }
+
+ public void forwardToPreviousPage(StaplerRequest request) throws ServletException, IOException {
+ }
+
+ public void sendRedirect2(String url) throws IOException {
+ }
+
+ public void serveFile(StaplerRequest request, URL res) throws ServletException, IOException {
+ }
+
+ public void serveFile(StaplerRequest request, URL res, long expiration) throws ServletException, IOException {
+ }
+
+ public void serveLocalizedFile(StaplerRequest request, URL res) throws ServletException, IOException {
+ }
+
+ public void serveLocalizedFile(StaplerRequest request, URL res, long expiration) throws ServletException, IOException {
+ }
+
+ public void serveFile(StaplerRequest req, InputStream data, long lastModified, long expiration, long contentLength, String fileName) throws
+ ServletException, IOException
+ {
+ }
+
+ public void serveFile(StaplerRequest req, InputStream data, long lastModified, long expiration, int contentLength, String fileName) throws
+ ServletException, IOException
+ {
+ }
+
+ public void serveFile(StaplerRequest req, InputStream data, long lastModified, long contentLength, String fileName) throws
+ ServletException, IOException
+ {
+ }
+
+ public void serveFile(StaplerRequest req, InputStream data, long lastModified, int contentLength, String fileName) throws
+ ServletException, IOException
+ {
+ }
+
+ public void serveExposedBean(StaplerRequest req, Object exposedBean, Flavor flavor) throws ServletException, IOException {
+ }
+
+ public OutputStream getCompressedOutputStream(HttpServletRequest req) throws IOException {
+ return null;
+ }
+
+ public Writer getCompressedWriter(HttpServletRequest req) throws IOException {
+ return null;
+ }
+
+ public int reverseProxyTo(URL url, StaplerRequest req) throws IOException {
+ return 0;
+ }
+
+ public void addCookie(Cookie cookie) {
+ }
+
+ public boolean containsHeader(String name) {
+ return false;
+ }
+
+ public String encodeURL(String url) {
+ return null;
+ }
+
+ public String encodeRedirectURL(String url) {
+ return null;
+ }
+
+ public String encodeUrl(String url) {
+ return null;
+ }
+
+ public String encodeRedirectUrl(String url) {
+ return null;
+ }
+
+ public void sendError(int sc, String msg) throws IOException {
+ }
+
+ public void sendError(int sc) throws IOException {
+ }
+
+ public void sendRedirect(String location) throws IOException {
+ }
+
+ public void setDateHeader(String name, long date) {
+ }
+
+ public void addDateHeader(String name, long date) {
+ }
+
+ public void setHeader(String name, String value) {
+ }
+
+ public void addHeader(String name, String value) {
+ }
+
+ public void setIntHeader(String name, int value) {
+ }
+
+ public void addIntHeader(String name, int value) {
+ }
+
+ public void setStatus(int sc) {
+ }
+
+ public void setStatus(int sc, String sm) {
+ }
+
+ public String getCharacterEncoding() {
+ return null;
+ }
+
+ public String getContentType() {
+ return null;
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException {
+ return null;
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ return null;
+ }
+
+ public void setCharacterEncoding(String charset) {
+ }
+
+ public void setContentLength(int len) {
+ }
+
+ public void setContentType(String type) {
+ }
+
+ public void setBufferSize(int size) {
+ }
+
+ public int getBufferSize() {
+ return 0;
+ }
+
+ public void flushBuffer() throws IOException {
+ }
+
+ public void resetBuffer() {
+ }
+
+ public boolean isCommitted() {
+ return false;
+ }
+
+ public void reset() {
+ }
+
+ public void setLocale(Locale loc) {
+ }
+
+ public Locale getLocale() {
+ return null;
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/NodeServiceImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/NodeServiceImpl.java
new file mode 100644
index 00000000..9d79409f
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/NodeServiceImpl.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.eclipse.hudson.service.internal.ServicePreconditions.*;
+
+import com.google.common.base.Preconditions;
+
+import hudson.model.Computer;
+import hudson.model.Node;
+import hudson.security.Permission;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.eclipse.hudson.service.NodeNotFoundException;
+import org.eclipse.hudson.service.NodeService;
+import org.eclipse.hudson.service.SecurityService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Default implementation of {@link NodeService}
+ *
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class NodeServiceImpl extends ServiceSupport implements NodeService {
+
+ private final SecurityService security;
+
+ @Inject
+ public NodeServiceImpl(final SecurityService security) {
+ this.security = Preconditions.checkNotNull(security);
+ }
+
+ public Node getNode(String nodeName) {
+ Node node = findNode(nodeName);
+ if (node == null) {
+ throw new NodeNotFoundException(String.format("Node %s not found.", nodeName));
+ }
+ return node;
+ }
+
+ public Node findNode(final String nodeName) {
+ log.debug("findNode(\"{}\")", nodeName);
+ checkNodeName(nodeName);
+ Node node;
+ // Provide the master node, if the name is ""
+ if ("".equals(nodeName)) {
+ node = getHudson();
+ } else {
+ node = getHudson().getNode(nodeName);
+ }
+ if (node != null) {
+ this.security.checkPermission(node, Permission.READ);
+ }
+ return node;
+ }
+
+ public Node getMasterNode() {
+ Node masterNode = getHudson();
+ this.security.checkPermission(masterNode, Permission.READ);
+ return masterNode;
+ }
+
+ public List<Node> getAllNodes() {
+ List<Node> nodesToReturn = getNodes();
+ Node masterNode = getHudson();
+ if (this.security.hasPermission(masterNode, Permission.READ)) {
+ nodesToReturn.add(masterNode);
+ }
+ return nodesToReturn;
+ }
+
+ public List<Node> getNodes() {
+ List<Node> nodesToCheck = getHudson().getNodes();
+ List<Node> nodesToReturn = new ArrayList<Node>(nodesToCheck.size());
+
+ for (hudson.model.Node node : nodesToCheck) {
+ if (this.security.hasPermission(node, Permission.READ)) {
+ nodesToReturn.add(node);
+ }
+ }
+ return nodesToReturn;
+ }
+
+ public Node getCurrentNode() {
+ Node node = Computer.currentComputer().getNode();
+ this.security.checkPermission(node, Permission.READ);
+ return node;
+ }
+
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ProjectServiceImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ProjectServiceImpl.java
new file mode 100644
index 00000000..0d896d00
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ProjectServiceImpl.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.eclipse.hudson.service.internal.ServicePreconditions.*;
+import hudson.matrix.MatrixConfiguration;
+import hudson.matrix.MatrixProject;
+import hudson.model.Item;
+import hudson.model.TopLevelItem;
+import hudson.model.AbstractProject;
+import hudson.model.Job;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.eclipse.hudson.service.ProjectNotFoundException;
+import org.eclipse.hudson.service.ProjectService;
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.ServiceRuntimeException;
+import org.eclipse.hudson.service.SystemIntegrityViolationException;
+import org.eclipse.hudson.utils.tasks.JobUuid;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * Default {@link ProjectService} implementation.
+ *
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class ProjectServiceImpl extends ServiceSupport implements ProjectService {
+ private final SecurityService securityService;
+
+ @Inject
+ ProjectServiceImpl(final SecurityService securityService) {
+ this.securityService = Preconditions.checkNotNull(securityService);
+ }
+
+
+ public AbstractProject<?, ?> getProject(final UUID id) {
+ AbstractProject<?, ?> project = findProject(id);
+ if (project == null) {
+ throw new ProjectNotFoundException(String.format("No project with UUID '%s' found.", id));
+ }
+ return project;
+ }
+
+ public AbstractProject<?, ?> getProject(final String projectName) {
+ AbstractProject<?, ?> project = findProject(projectName);
+ if (project == null) {
+ throw new ProjectNotFoundException(String.format("Project %s not found.", projectName));
+ }
+ return project;
+ }
+
+ public AbstractProject<?, ?> getProjectByFullName(final String projectName) {
+ AbstractProject<?, ?> p = findProjectByFullName(projectName);
+ if (p == null) {
+ throw new ProjectNotFoundException(String.format("Project %s not found.", projectName));
+ }
+ return p;
+ }
+
+ public AbstractProject<?, ?> findProject(final UUID id) {
+ checkNotNull(id, UUID.class);
+ Job<?, ?> job = JobUuid.find(id);
+ return (AbstractProject<?, ?>) job;
+ }
+
+ public AbstractProject<?, ?> findProject(final String projectName) {
+ checkProjectName(projectName);
+
+ // Handle matrix-project/configuration projects.
+ // First part is matrix-project name, second part is
+ // matrix-configuration name
+ String[] parts = projectName.split("/", 2);
+ if (parts.length == 2) {
+ log.debug("Detected matrix name: {}", projectName);
+ AbstractProject<?, ?> parent = findProject(parts[0]);
+ if (parent instanceof MatrixProject) {
+ for (MatrixConfiguration config : ((MatrixProject) parent).getItems()) {
+ if (parts[1].equals(config.getName())) {
+ log.debug("Selected matrix configuration: {}", config);
+ // config.checkPermission(Item.READ); // TODO needed?
+ return config;
+ }
+ }
+ }
+ }
+ if (!getProjectNames().contains(projectName)) {
+ log.debug("Project {} not in the list of job names.", projectName);
+ return null;
+ }
+ AbstractProject<?, ?> project = getProjectByFullName(projectName);
+ log.debug("Selected project: {}", project);
+ return project;
+ }
+
+ public AbstractProject<?, ?> findProjectByFullName(final String projectName) {
+ checkProjectName(projectName);
+ AbstractProject<?, ?> p = getHudson().getItemByFullName(projectName, AbstractProject.class);
+ if (p != null) {
+ this.securityService.checkPermission(p, Item.READ);
+ }
+ return p;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public List<AbstractProject> getAllProjects() {
+ // Hudson checks that items are readable by the current context (has Item.READ perms)
+ return getHudson().getAllItems(AbstractProject.class);
+ }
+
+ public <T extends AbstractProject<?, ?>> T copyProject(final T src, final String targetProjectName)
+ throws ServiceRuntimeException {
+ checkNotNull(src, AbstractProject.class);
+ checkProjectName(targetProjectName);
+
+ this.securityService.checkPermission(Item.CREATE);
+ this.securityService.checkPermission(src, Item.EXTENDED_READ);
+ // caller should try to verify this themselves before calling me
+ // TODO should this check really be performed here?
+ if (projectExists(targetProjectName)) {
+ throw new SystemIntegrityViolationException(String.format("Project %s already exists.", targetProjectName));
+
+ }
+
+ try {
+ return getHudson().copy(src, targetProjectName);
+ } catch (IOException ex) {
+ throw new ServiceRuntimeException(String.format("Project copy failed from %s to %s", src.getName(),
+ targetProjectName), ex);
+ }
+ }
+
+ public TopLevelItem createProjectFromXML(final String projectName, final InputStream xml) {
+ checkProjectName(projectName);
+ checkNotNull(xml, InputStream.class);
+
+ this.securityService.checkPermission(Item.CREATE);
+
+ // caller should verify this themselves before calling me
+ // TODO should this check really be performed here?
+ if (projectExists(projectName)) {
+ throw new SystemIntegrityViolationException(String.format("Project %s already exists.", projectName));
+ }
+
+ try {
+ return getHudson().createProjectFromXML(projectName, xml);
+ } catch (IOException ex) {
+ throw new ServiceRuntimeException(String.format("Project creation failed for %s", projectName), ex);
+ }
+ }
+
+ public boolean projectExists(final String projectName) {
+ checkProjectName(projectName);
+ // This only checks names that are readable by the current context (has Item.READ perms)
+ return getProjectNames().contains(projectName);
+ }
+
+ public Collection<String> getProjectNames() {
+ // Hudson only returns jobnames when the current context has Item.READ perms on it
+ return getHudson().getJobNames();
+ }
+
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/QueueServiceImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/QueueServiceImpl.java
new file mode 100644
index 00000000..927c3cb6
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/QueueServiceImpl.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static com.google.common.base.Preconditions.*;
+
+import hudson.model.Hudson;
+import hudson.model.Queue;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.eclipse.hudson.service.QueueService;
+import org.eclipse.hudson.service.SecurityService;
+
+/**
+ * Default {@link QueueService} implementation.
+ *
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class QueueServiceImpl
+ extends ServiceSupport
+ implements QueueService
+{
+ private final SecurityService security;
+
+ @Inject
+ QueueServiceImpl(final SecurityService securityService) {
+ this.security = checkNotNull(securityService);
+ }
+
+ public Queue getQueue() {
+ this.security.checkPermission(Hudson.ADMINISTER);
+ return getHudson().getQueue();
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ScriptServiceImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ScriptServiceImpl.java
new file mode 100644
index 00000000..f5a3e387
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ScriptServiceImpl.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import hudson.model.Hudson;
+import hudson.model.Hudson.MasterComputer;
+import hudson.remoting.VirtualChannel;
+import hudson.util.RemotingDiagnostics;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.eclipse.hudson.service.ScriptService;
+import org.eclipse.hudson.service.SecurityService;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default implementation of {@link ScriptService}.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class ScriptServiceImpl
+ extends ServiceSupport
+ implements ScriptService
+{
+ private final SecurityService security;
+
+ @Inject
+ public ScriptServiceImpl(final SecurityService security) {
+ this.security = checkNotNull(security);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see RemotingDiagnostics#executeGroovy(String, VirtualChannel)
+ */
+ public String execute(final String script) throws Exception {
+ checkNotNull(script);
+ security.checkPermission(Hudson.ADMINISTER);
+ log.debug("Executing script on master: {}", script);
+ return RemotingDiagnostics.executeGroovy(script, MasterComputer.localChannel);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/SecurityServiceImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/SecurityServiceImpl.java
new file mode 100644
index 00000000..ef1db2f8
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/SecurityServiceImpl.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import hudson.model.User;
+import hudson.security.AccessControlled;
+import hudson.security.Permission;
+import org.acegisecurity.Authentication;
+import org.acegisecurity.context.SecurityContext;
+import org.acegisecurity.context.SecurityContextHolder;
+import org.eclipse.hudson.service.SecurityService;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.util.concurrent.Callable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default {@link SecurityService} implementation.
+ *
+ * @author plynch
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class SecurityServiceImpl
+ extends ServiceSupport
+ implements SecurityService
+{
+ @Inject
+ SecurityServiceImpl() {
+ super();
+ }
+
+ public void checkPermission(final Permission permission) {
+ getHudson().checkPermission(permission);
+ }
+
+ public User getCurrentUser() {
+ return User.current();
+ }
+
+ public User getUser(final String id) {
+ // id may be null
+ return getHudson().getUser(id);
+ }
+
+ public User getUnknownUser() {
+ return User.getUnknown();
+ }
+
+ public void runAs(final Authentication auth, final Runnable task) {
+ checkNotNull(auth);
+ checkNotNull(task);
+ final SecurityContext ctx = SecurityContextHolder.getContext();
+ final Authentication current = ctx.getAuthentication();
+ ctx.setAuthentication(auth);
+ try {
+ task.run();
+ }
+ finally {
+ ctx.setAuthentication(current);
+ }
+ }
+
+ public <T> T callAs(final Authentication auth, final Callable<T> task) throws Exception {
+ checkNotNull(auth);
+ checkNotNull(task);
+ final SecurityContext ctx = SecurityContextHolder.getContext();
+ final Authentication current = ctx.getAuthentication();
+ ctx.setAuthentication(auth);
+ try {
+ return task.call();
+ }
+ finally {
+ ctx.setAuthentication(current);
+ }
+ }
+
+ public <T> T callAs2(final Authentication auth, final Callable<T> task) {
+ try {
+ return callAs(auth, task);
+ }
+ catch (Exception e) {
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ }
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void checkPermission(final AccessControlled controlled, final Permission permission) {
+ checkNotNull(controlled);
+ checkNotNull(permission);
+ controlled.checkPermission(permission);
+ }
+
+ public boolean hasPermission(final AccessControlled controlled, final Permission permission) {
+ checkNotNull(controlled);
+ checkNotNull(permission);
+ return controlled.hasPermission(permission);
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ServicePreconditions.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ServicePreconditions.java
new file mode 100644
index 00000000..17922fe3
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ServicePreconditions.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static com.google.common.base.Preconditions.*;
+
+import java.util.UUID;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Preconditions to check common arguments to Service API calls
+ *
+ * @since 2.1.0
+ */
+public abstract class ServicePreconditions {
+
+ protected static final Logger log = LoggerFactory.getLogger(ServicePreconditions.class);
+
+ // should not normally instantiate
+ protected ServicePreconditions() {
+ }
+
+ /**
+ * Check a project buildNumber for being greater than zero.
+ *
+ * @param buildNumber buildNumber to test for validity
+ * @return the passed in argument if valid
+ * @throws IllegalArgumentException if buildNumber is less than one
+ */
+ public static int checkBuildNumber(final int buildNumber) {
+ checkArgument(buildNumber > 0, "buildNumber (%s) must be greater than zero.", buildNumber);
+ return buildNumber;
+ }
+
+ /**
+ * Check a project build state index for shallow validity.
+ *
+ * @param index a value that should be non-negative
+ * @return the passed in argument if valid
+ * @throws IllegalArgumentException if index is negative
+ */
+ public static int checkBuildStateIndex(final int index) {
+ checkArgument(index >= 0, "build state index (%s) must be greater than zero.", index);
+ return index;
+ }
+
+ /**
+ * Check a project builder index for shallow validity
+ *
+ * @param index a value that should be non-negative
+ * @return the passed in argument if valid
+ * @throws IllegalArgumentException if index is negative
+ */
+ public static int checkBuilderIndex(final int index) {
+ checkArgument(index >= 0, "builder index (%s) must be greater than zero.", index);
+ return index;
+ }
+
+ /**
+ * Check the projectName of an {@link hudson.model.AbstractProject} for shallow validity
+ *
+ * @param projectName the project name to check
+ * @return the unmodified projectName if not null
+ * @throws NullPointerException if projectName is null
+ */
+ public static String checkProjectName(final String projectName) {
+ return checkNotNull(projectName, "project name");
+ }
+
+ /**
+ * Check the nodeName of an {@link hudson.model.Node} for shallow validity
+ *
+ * @param nodeName the node name to check
+ * @return the unmodified nodeName if not null
+ * @throws NullPointerException if nodeName is null
+ */
+ public static String checkNodeName(final String nodeName) {
+ // FIXME: Probably need to validate encode/decode the nodeName
+ return checkNotNull(nodeName, "node name");
+ }
+
+ /**
+ * Check a {@link org.hudsonci.rest.model.DocumentDTO} ID (UUID) for
+ * shallow validity
+ *
+ * @param id the Document id to check
+ * @return the unmodified document id
+ * @throws NullPointerException if document id is null
+ * @throws IllegalArgumentException if document id is not in the expected
+ * format of UUID
+ */
+ public static String checkDocumentId(final String id) {
+ checkNotNull(id, "Document ID");
+ checkUUID(id);
+ return id;
+ }
+
+ /**
+ * Check a uuid string for validity.
+ *
+ * @param uuid the argument to check for validity
+ * @return the uuid argument unmodified
+ * @throws IllegalArgumentException if uuid cannot be converted to a UUID
+ * according to {@link java.util.UUID#fromString(String)}
+ */
+ public static String checkUUID(final String uuid) {
+ UUID.fromString(uuid);
+ return uuid;
+ }
+
+ /**
+ * Check an argument for null. If it is null, then throw
+ * NullPointerException
+ *
+ * @param reference a reference to what we are checking for null
+ * @param msgKey a name describing what we are checking for null, to be
+ * added to the NullPointerException msg in case of error
+ */
+ public static <T> T checkNotNull(T reference, String msgKey) {
+ return Preconditions.checkNotNull(reference, "%s must not be null.", msgKey);
+ }
+
+ /**
+ * IF the passed reference is null, throws a NullPointerException with
+ * appropriate message.
+ * <p>
+ * While not itself a precondition, this is a common operation if a
+ * precondition fails and can be used when custom validation was performed
+ * for a rest argument.
+ *
+ * @param <T> reference to a value to check for null
+ * @param reference reference to a value to check for null
+ * @param clazz the type of reference, may be null
+ * @return reference unchanged
+ * @throws NullPointerException with message derived from clazz value if
+ * reference is null
+ */
+ public static <T> T checkNotNull(T reference, Class<T> clazz) {
+ if (reference == null) {
+ final String msg = clazz == null ? "Required value" : clazz.getName() + " must not be null.";
+ throw new NullPointerException(msg);
+ }
+ return reference;
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ServiceSupport.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ServiceSupport.java
new file mode 100644
index 00000000..1a8d838f
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/ServiceSupport.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import hudson.model.Hudson;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Base Hudson Service from which all service implementations should extend.
+ *
+ * <p>Common {@literal preconditions} of service implementation methods
+ * include:
+ *
+ * <ul>
+ * <li>throw a {@link NullPointerException} if a null object
+ * reference is passed in any parameter.
+ * <li>throw an {@link org.acegisecurity.AccessDeniedException} if the current thread context does not hold a required authority
+ * to perform an operation
+ * </ul>
+ *
+ * @author plynch
+ * @since 2.1.0
+ */
+public abstract class ServiceSupport
+{
+ protected final Logger log = LoggerFactory.getLogger(getClass());
+
+ private Hudson hudson;
+
+ @Inject
+ public void setHudson(final Hudson hudson) {
+ this.hudson = checkNotNull(hudson);
+ }
+
+ protected Hudson getHudson()
+ {
+ return hudson;
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/SystemServiceImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/SystemServiceImpl.java
new file mode 100644
index 00000000..db7516c4
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/SystemServiceImpl.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.ServiceRuntimeException;
+import org.eclipse.hudson.service.SystemService;
+import org.eclipse.hudson.utils.io.FileUtil;
+
+import hudson.XmlFile;
+import hudson.init.InitMilestone;
+import hudson.lifecycle.RestartNotSupportedException;
+import hudson.model.Hudson;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default {@link SystemService} implementation
+ *
+ * @author plynch
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class SystemServiceImpl
+ extends ServiceSupport
+ implements SystemService
+{
+ private final SecurityService securityService;
+
+ @Inject
+ SystemServiceImpl(final SecurityService securityService) {
+ this.securityService = checkNotNull(securityService);
+ }
+
+ public File getInstallationDirectory() {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ File dir;
+ try {
+ // verbose to help pinpoint any NPE at runtime
+ ProtectionDomain pd = Hudson.class.getProtectionDomain();
+ CodeSource cs = pd.getCodeSource();
+ URL url = cs.getLocation();
+ String path = url.getPath();
+ dir = new File(path);
+ // Jar containing Launcher is expected in <install>/lib/some.jar (so .jar file - lib dir - should get us the install dir)
+ dir = dir.getParentFile().getParentFile();
+ dir = FileUtil.canonicalize(dir);
+ }
+ catch (NullPointerException e) {
+ throw new IllegalStateException("Could not reliably determine the installation directory", e);
+ }
+ return dir;
+ }
+
+ public File getLogDirectory() {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ // From the installation directory these are located at var/log
+ File file = new File(getInstallationDirectory(), "var/log");
+ file = FileUtil.canonicalize(file);
+ return file;
+ }
+
+ public File getWorkingDirectory() {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ return getHudson().getRootDir();
+ }
+
+ public XmlFile getConfigFile()
+ {
+ securityService.checkPermission(Hudson.ADMINISTER);
+ // Hudson.getConfigFile() is not public, so we have to duplicate some muck here
+ File f = new File(getWorkingDirectory(), "config.xml");
+ return new XmlFile(Hudson.XSTREAM, f);
+ }
+
+ public String getUrl()
+ {
+ String url = getHudson().getRootUrl();
+
+ if (url == null) {
+ log.warn("Underlying Hudson root url is null; using DEFAULT_URL");
+ url = DEFAULT_URL;
+ }
+ else if (url.endsWith("/")) {
+ url = url.substring(0, url.length() - 1);
+ }
+
+ return url;
+ }
+
+ public String getVersion()
+ {
+ return Hudson.getVersion().toString();
+ }
+
+ public InitMilestone getInitLevel()
+ {
+ return getHudson().getInitLevel();
+ }
+
+ public boolean isQuietingDown()
+ {
+ return getHudson().isQuietingDown();
+ }
+
+ public boolean isTerminating()
+ {
+ return getHudson().isTerminating();
+ }
+
+ public String getSystemMessage()
+ {
+ return getHudson().getSystemMessage();
+ }
+
+ public void doQuietDown()
+ {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ try{getHudson().doQuietDown();}catch(final IOException e){}
+ }
+
+ public void doQuietDown(boolean toggle)
+ {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ if (toggle)
+ {
+ log.debug("Quieting down");
+ doQuietDown();
+ }
+ else
+ {
+ log.debug("Canceling quiet down");
+ doCancelQuietDown();
+ }
+ }
+
+ public void doCancelQuietDown()
+ {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ getHudson().doCancelQuietDown();
+ }
+
+ public void doReload()
+ {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ log.debug("Reloading configuration");
+ try
+ {
+ getHudson().doReload();
+ }
+ catch (IOException ex)
+ {
+ throw new ServiceRuntimeException("Could not reload.", ex);
+ }
+ }
+
+ public void doRestart(boolean safely)
+ {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ try
+ {
+ if (safely)
+ {
+ log.debug("Restarting (safely)");
+ getHudson().safeRestart();
+
+ }
+ else
+ {
+ log.debug("Restarting");
+ getHudson().restart();
+ }
+ }
+ catch (RestartNotSupportedException ex)
+ {
+ throw new ServiceRuntimeException("Restart not supported", ex);
+ }
+ }
+
+ public void doRestart()
+ {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ log.debug("Restarting");
+ try
+ {
+ getHudson().safeRestart();
+ }
+ catch (RestartNotSupportedException ex)
+ {
+ throw new ServiceRuntimeException("Restart not supported", ex);
+ }
+ }
+
+ public void doRestartSafely()
+ {
+ //securityService.checkPermission(Hudson.ADMINISTER);
+ log.debug("Restarting (safely)");
+ try
+ {
+ getHudson().restart();
+ }
+ catch (RestartNotSupportedException ex)
+ {
+ throw new ServiceRuntimeException("Restart not supported", ex);
+ }
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/internal/package-info.java b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/package-info.java
new file mode 100644
index 00000000..fcbafd63
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/internal/package-info.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/service/package-info.java b/hudson-service/src/main/java/org/eclipse/hudson/service/package-info.java
new file mode 100644
index 00000000..8b5d25c4
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/service/package-info.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service;
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletContainer.java b/hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletContainer.java
new file mode 100644
index 00000000..003127df
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletContainer.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.servlets;
+
+import com.google.inject.ImplementedBy;
+
+import org.eclipse.hudson.servlets.internal.ServletContainerImpl;
+
+/**
+ * Provides access to register servlets.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@ImplementedBy(ServletContainerImpl.class)
+public interface ServletContainer
+{
+ void start() throws Exception;
+
+ void stop() throws Exception;
+
+ ServletRegistration.Handle register(ServletRegistration registration) throws Exception;
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletContainerAware.java b/hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletContainerAware.java
new file mode 100644
index 00000000..166914d8
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletContainerAware.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.servlets;
+
+import hudson.ExtensionPoint;
+
+/**
+ * Extension to allow plugins to be aware of the {@link ServletContainer} and register servlets.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+public interface ServletContainerAware
+ extends ExtensionPoint
+{
+ void setServletContainer(ServletContainer container) throws Exception;
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletRegistration.java b/hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletRegistration.java
new file mode 100644
index 00000000..3330291e
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/servlets/ServletRegistration.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.servlets;
+
+import javax.servlet.Servlet;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@link Servlet} registration details.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+public class ServletRegistration
+ implements Cloneable
+{
+ private String name;
+
+ private Class<? extends Servlet> servletType;
+
+ private Servlet servlet;
+
+ private String uriPrefix;
+
+ private Map<String, String> parameters;
+
+ public String getName() {
+ return name;
+ }
+
+ public ServletRegistration setName(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Class<? extends Servlet> getServletType() {
+ return servletType;
+ }
+
+ public ServletRegistration setServletType(final Class<? extends Servlet> type) {
+ this.servletType = type;
+ return this;
+ }
+
+ public Servlet getServlet() {
+ return servlet;
+ }
+
+ public ServletRegistration setServlet(final Servlet servlet) {
+ this.servlet = servlet;
+ return this;
+ }
+
+ public String getUriPrefix() {
+ return uriPrefix;
+ }
+
+ public ServletRegistration setUriPrefix(final String path) {
+ this.uriPrefix = path;
+ return this;
+ }
+
+ public Map<String, String> getParameters() {
+ if (parameters == null) {
+ return new HashMap<String, String>();
+ }
+ return parameters;
+ }
+
+ public ServletRegistration setParameters(final Map<String, String> parameters) {
+ this.parameters = parameters;
+ return this;
+ }
+
+ public ServletRegistration addParameter(final String name, final String value) {
+ getParameters().put(name, value);
+ return this;
+ }
+
+ @Override
+ public ServletRegistration clone() {
+ try {
+ return (ServletRegistration) super.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ throw (InternalError) new InternalError().initCause(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ServletRegistration{" +
+ "name='" + name + '\'' +
+ ", servletType=" + servletType +
+ ", servlet=" + servlet +
+ ", uriPrefix='" + uriPrefix + '\'' +
+ ", parameters=" + parameters +
+ '}';
+ }
+
+ public static interface Handle
+ {
+ ServletRegistration getRegistration();
+
+ void setEnabled(boolean enabled);
+
+ boolean isEnabled();
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletContainerImpl.java b/hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletContainerImpl.java
new file mode 100644
index 00000000..d174c3ee
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletContainerImpl.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.servlets.internal;
+
+import org.eclipse.hudson.servlets.ServletContainer;
+import org.eclipse.hudson.servlets.ServletContainerAware;
+import org.eclipse.hudson.servlets.ServletRegistration;
+
+import hudson.util.PluginServletFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import javax.servlet.Filter;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default {@link ServletContainer} implementation.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class ServletContainerImpl
+ implements ServletContainer
+{
+ private static final Logger log = LoggerFactory.getLogger(ServletContainerImpl.class);
+
+ private final Map<ServletRegistration, Filter> registrations = new LinkedHashMap<ServletRegistration, Filter>();
+
+ private final List<ServletContainerAware> concerned;
+
+ @Inject
+ public ServletContainerImpl(final List<ServletContainerAware> concerned) {
+ this.concerned = checkNotNull(concerned);
+ }
+
+ private ServletContainerAware[] getConcerned() {
+ return concerned.toArray(new ServletContainerAware[concerned.size()]);
+ }
+
+ public void start() throws Exception {
+ log.debug("Starting");
+
+ // When a ServletContainerAware ext gets executed, we need to set the TCL so that it loads properly, else crazy things will happen
+ final ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+ for (ServletContainerAware target : getConcerned()) {
+ Thread.currentThread().setContextClassLoader(target.getClass().getClassLoader());
+ try {
+ target.setServletContainer(this);
+ }
+ catch (Exception e) {
+ log.error("Exception while exposing servlet container to: " + target, e);
+ }
+ finally {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+ }
+
+ public void stop() throws Exception {
+ log.debug("Stopping");
+
+ for (Filter filter : registrations.values()) {
+ PluginServletFilter.removeFilter(filter);
+ }
+
+ registrations.clear();
+ }
+
+ public ServletRegistration.Handle register(ServletRegistration registration) throws Exception {
+ checkNotNull(registration);
+
+ log.debug("Registering: {}", registration);
+ registration = registration.clone();
+
+ ServletRegistrationFilterAdapter filter = new ServletRegistrationFilterAdapter(registration);
+ PluginServletFilter.addFilter(filter);
+
+ registrations.put(registration, filter);
+
+ return new HandleImpl(registration, filter);
+ }
+
+ private class HandleImpl
+ implements ServletRegistration.Handle
+ {
+ private final ServletRegistration registration;
+
+ private final ServletRegistrationFilterAdapter filterAdapter;
+
+ private HandleImpl(final ServletRegistration registration, final ServletRegistrationFilterAdapter filterAdapter) {
+ this.registration = checkNotNull(registration);
+ this.filterAdapter = checkNotNull(filterAdapter);
+ }
+
+ public ServletRegistration getRegistration() {
+ return registration;
+ }
+
+ public void setEnabled(final boolean enabled) {
+ filterAdapter.setEnabled(enabled);
+ }
+
+ public boolean isEnabled() {
+ return filterAdapter.isEnabled();
+ }
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletContainerStarter.java b/hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletContainerStarter.java
new file mode 100644
index 00000000..1bfa8707
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletContainerStarter.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.servlets.internal;
+
+import org.eclipse.hudson.servlets.ServletContainer;
+
+import hudson.model.listeners.ItemListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Starts the {@link ServletContainer}.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+@Named
+@Singleton
+public class ServletContainerStarter
+ extends ItemListener
+{
+ private static final Logger log = LoggerFactory.getLogger(ServletContainerStarter.class);
+
+ // FIXME: Use of ItemListener for server life-cycle bits is a work around
+
+ private final ServletContainer servlets;
+
+ @Inject
+ public ServletContainerStarter(final ServletContainer servlets) {
+ this.servlets = checkNotNull(servlets);
+ }
+
+ @Override
+ public void onLoaded() {
+ try {
+ servlets.start();
+ }
+ catch (Exception e) {
+ log.error("Failed to start servlet container", e);
+ }
+ }
+}
diff --git a/hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletRegistrationFilterAdapter.java b/hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletRegistrationFilterAdapter.java
new file mode 100644
index 00000000..3c3df6b5
--- /dev/null
+++ b/hudson-service/src/main/java/org/eclipse/hudson/servlets/internal/ServletRegistrationFilterAdapter.java
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.servlets.internal;
+
+import org.eclipse.hudson.servlets.ServletRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Wraps a {@link Servlet} as a {@link Filter} for installation via {@link hudson.util.PluginServletFilter}.
+ *
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.1.0
+ */
+public class ServletRegistrationFilterAdapter
+ implements Filter
+{
+ private static final Logger log = LoggerFactory.getLogger(ServletRegistrationFilterAdapter.class);
+
+ private final ServletRegistration registration;
+
+ private final Servlet servlet;
+
+ private final String uriPrefix;
+
+ private boolean enabled;
+
+ public ServletRegistrationFilterAdapter(final ServletRegistration registration) throws Exception {
+ this.registration = checkNotNull(registration);
+ this.servlet = createServlet();
+
+ if (registration.getName() == null) {
+ registration.setName(servlet.getClass().getName());
+ }
+
+ uriPrefix = registration.getUriPrefix();
+ if (uriPrefix == null) {
+ throw new IllegalArgumentException("Registration missing uriPrefix");
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(final boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ private Servlet createServlet() throws Exception {
+ Servlet servlet = registration.getServlet();
+ if (servlet != null) {
+ return servlet;
+ }
+
+ Class<? extends Servlet> type = registration.getServletType();
+ if (type != null) {
+ return type.newInstance();
+ }
+
+ throw new IllegalArgumentException("Registration missing servlet or servlet type");
+ }
+
+ public void init(final FilterConfig config) throws ServletException {
+ checkNotNull(config);
+
+ servlet.init(new ServletConfig()
+ {
+ public String getServletName() {
+ return registration.getName();
+ }
+
+ public ServletContext getServletContext() {
+ return config.getServletContext();
+ }
+
+ public Enumeration getInitParameterNames() {
+ final Iterator<String> iter = registration.getParameters().keySet().iterator();
+
+ return new Enumeration()
+ {
+ public boolean hasMoreElements() {
+ return iter.hasNext();
+ }
+
+ public Object nextElement() {
+ return iter.next();
+ }
+ };
+ }
+
+ public String getInitParameter(final String name) {
+ return registration.getParameters().get(name);
+ }
+ });
+ }
+
+ public void destroy() {
+ servlet.destroy();
+ }
+
+ public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
+ throws IOException, ServletException
+ {
+ assert chain != null;
+
+ if (isEnabled() && request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
+ doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
+ }
+ else {
+ chain.doFilter(request, response);
+ }
+ }
+
+ private void doFilter(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain)
+ throws IOException, ServletException
+ {
+ assert request != null;
+ assert response != null;
+ assert chain != null;
+
+ String contextPath = request.getContextPath();
+ if (!contextPath.endsWith("/") && !uriPrefix.startsWith("/")) {
+ contextPath = contextPath + '/';
+ }
+
+ if (request.getRequestURI().startsWith(contextPath + uriPrefix)) {
+ // Wrap the request to augment the servlet uriPrefix
+ HttpServletRequestWrapper req = new HttpServletRequestWrapper(request)
+ {
+ @Override
+ public String getServletPath() {
+ return String.format("/%s", uriPrefix);
+ }
+ };
+
+ servlet.service(req, response);
+ }
+ else {
+ chain.doFilter(request, response);
+ }
+ }
+}
diff --git a/hudson-service/src/test/java/org/eclipse/hudson/service/internal/BuildServiceImplTest.java b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/BuildServiceImplTest.java
new file mode 100644
index 00000000..3b5a620c
--- /dev/null
+++ b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/BuildServiceImplTest.java
@@ -0,0 +1,394 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+import hudson.model.Item;
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+import hudson.model.Run;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.eclipse.hudson.service.BuildNotFoundException;
+import org.eclipse.hudson.service.BuildService;
+import org.eclipse.hudson.service.ProjectService;
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.ServiceRuntimeException;
+import org.eclipse.hudson.service.internal.BuildServiceImpl;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class BuildServiceImplTest {
+
+ @Mock
+ private ProjectService projectService;
+
+ @Mock
+ private SecurityService securityService;
+
+ @Mock
+ private AbstractProject<?, ?> project;
+
+ @Mock
+ private AbstractBuild<?, ?> build;
+
+ private BuildService getInst() {
+ return new BuildServiceImpl(projectService, securityService);
+ }
+
+ @Test
+ public void getInstNotNull() {
+ assertNotNull(getInst());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void constructorNullArg1() {
+ new BuildServiceImpl(null, securityService);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void constructorNullArg2() {
+ new BuildServiceImpl(projectService, null);
+ }
+
+ // deleteBuild --------------
+ @Test(expected = NullPointerException.class)
+ public void deleteBuildNullProject() {
+ getInst().deleteBuild(null, 1);
+
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void deleteBuildNegativeBuildNumber() {
+ getInst().deleteBuild(project, -1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void deleteBuildZeroBuildNumber() {
+ getInst().deleteBuild(project, 0);
+ }
+
+ @Test
+ public void deleteBuildSecurity() throws IOException {
+
+ // spy so that we can call real methods
+ BuildService buildService = spy(getInst());
+
+ // partial mocking, using doReturn to avoid type safety <?,?> BS
+ doReturn(build).when(buildService).getBuild(project, 1);
+
+ // test
+ buildService.deleteBuild(project, 1);
+
+ // verify, security before operation
+ InOrder inOrder = inOrder(securityService, build);
+ inOrder.verify(securityService).checkPermission(build, Run.DELETE);
+ inOrder.verify(build).delete();
+
+ }
+
+ @Test(expected = ServiceRuntimeException.class)
+ public void deleteBuildServiceRuntimeException() throws IOException {
+ // spy so that we can call real methods
+ BuildService buildService = spy(getInst());
+
+ // partial mocking, using doReturn to avoid type safety <?,?> BS
+ doReturn(build).when(buildService).getBuild(project, 1);
+ doThrow(new IOException()).when(build).delete();
+
+ // test
+ buildService.deleteBuild(project, 1);
+
+ }
+
+ // keepBuild --------------
+
+ @Test(expected = NullPointerException.class)
+ public void keepBuildNullArg1() {
+ getInst().keepBuild(null, 1, true);
+
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void keepBuildNegativeBuildNumber() {
+ getInst().keepBuild(project, -1, true);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void keepBuildZeroBuildNumber() {
+ getInst().keepBuild(project, 0, true);
+ }
+
+ @Test
+ public void keepBuildSecurity() throws IOException {
+
+ // spy so that we can call real methods
+ BuildService buildService = spy(getInst());
+
+ // partial mocking, using doReturn to avoid type safety <?,?> BS
+ doReturn(build).when(buildService).getBuild(project, 1);
+
+ // test
+ buildService.keepBuild(project, 1, true);
+
+ // verify, security before operation
+ InOrder inOrder = inOrder(securityService, build);
+ inOrder.verify(securityService).checkPermission(build, Run.UPDATE);
+ inOrder.verify(build).keepLog(false);
+
+ }
+
+ @Test(expected = ServiceRuntimeException.class)
+ public void keepBuildServiceRuntimeException() throws IOException {
+ // spy so that we can call real methods
+ BuildService buildService = spy(getInst());
+
+ // partial mocking, using doReturn to avoid type safety <?,?> BS
+ doReturn(build).when(buildService).getBuild(project, 1);
+ doThrow(new IOException()).when(build).keepLog(false);
+
+ // test
+ buildService.keepBuild(project, 1, true);
+
+ }
+
+ // getBuild(AbstractProject) -----------
+
+ @Test(expected = NullPointerException.class)
+ public void getBuildProjectNullProject() {
+ getInst().getBuild((AbstractProject<?, ?>) null, 1);
+
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void getBuildProjectBuildNumber() {
+ getInst().getBuild(project, -1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void getBuildProjectZeroBuildNumber() {
+ getInst().getBuild(project, 0);
+ }
+
+ @Test
+ public void getBuildByProjectSecurity() throws IOException {
+
+ // spy so that we can call real methods
+ BuildService buildService = spy(getInst());
+
+ // partial mocking
+ doReturn(build).when(project).getBuildByNumber(1);
+
+ // test
+ buildService.getBuild(project, 1);
+
+ // verify
+ verify(securityService).checkPermission(build, Item.READ);
+
+ }
+
+ @Test(expected = BuildNotFoundException.class)
+ public void getBuildByProjectBuildNotFoundException() throws IOException {
+ // spy so that we can call real methods
+ getInst().getBuild(project, 1);
+ }
+
+ // getBuild(projectName) -----------
+
+ @Test(expected = NullPointerException.class)
+ public void getBuildProjectNameNullName() {
+ getInst().getBuild((String) null, 1);
+
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void getBuildProjectNameNegativeBuildNumber() {
+ getInst().getBuild("projectName", -1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void getBuildProjectNameZeroBuildNumber() {
+ getInst().getBuild("projectName", 0);
+ }
+
+ @Test
+ public void getBuildByProjectNameSecurity() throws IOException {
+
+ // spy so that we can call real methods
+ BuildService buildService = spy(getInst());
+
+ // partial mocking
+ doReturn(build).when(project).getBuildByNumber(1);
+ doReturn(project).when(projectService).getProject("projectName");
+
+ // test
+ buildService.getBuild("projectName", 1);
+
+ // verify
+ verify(securityService).checkPermission(build, Item.READ);
+
+ }
+
+ @Test(expected = BuildNotFoundException.class)
+ public void getBuildByProjectNameBuildNotFoundException() throws IOException {
+ doReturn(project).when(projectService).getProject("projectName");
+ // spy so that we can call real methods
+ getInst().getBuild("projectName", 1);
+ }
+
+ // findBuildByProject ------
+
+ @Test(expected = NullPointerException.class)
+ public void findBuildByProjectNullProject() {
+ getInst().findBuild((AbstractProject<?, ?>) null, 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findBuildByProjectBuildNumber() {
+ getInst().findBuild(project, -1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findBuildByProjectZeroBuildNumber() {
+ getInst().findBuild(project, 0);
+ }
+
+ @Test
+ public void findBuildByProjectSecurity() throws IOException {
+
+ // spy so that we can call real methods
+ BuildService buildService = spy(getInst());
+
+ // partial mocking
+ doReturn(build).when(project).getBuildByNumber(1);
+
+ // test
+ buildService.findBuild(project, 1);
+
+ // verify
+ verify(securityService).checkPermission(build, Item.READ);
+
+ }
+
+ @Test
+ public void findBuildByProjectReturnNull() {
+ assertNull(getInst().findBuild(project, 1));
+ }
+
+ // findBuildByProjectName -----
+
+ @Test(expected = NullPointerException.class)
+ public void findBuildProjectNameNullName() {
+ getInst().findBuild((String) null, 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findBuildProjectNameNegativeBuildNumber() {
+ getInst().findBuild("projectName", -1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findBuildProjectNameZeroBuildNumber() {
+ getInst().findBuild("projectName", 0);
+ }
+
+ @Test
+ public void findBuildByProjectNameSecurity() throws IOException {
+
+ // spy so that we can call real methods
+ BuildService buildService = spy(getInst());
+
+ // partial mocking
+ doReturn(project).when(projectService).findProject("projectName");
+ doReturn(build).when(project).getBuildByNumber(1);
+
+ // test
+ buildService.findBuild("projectName", 1);
+
+ // verify
+ verify(securityService).checkPermission(build, Item.READ);
+
+ }
+
+ @Test
+ public void findBuildByProjectNameReturnNull() {
+ assertNull(getInst().findBuild("projectName", 1));
+ }
+
+
+ // stopBuild -------------
+
+ @Test(expected = NullPointerException.class)
+ public void stopBuildByProjectNameNullName() {
+ getInst().stopBuild(null, 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void stopBuildByProjectNameNegativeBuildNumber() {
+ getInst().stopBuild(project, -1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void stopBuildProjectNameZeroBuildNumber() {
+ getInst().stopBuild(project, 0);
+ }
+
+ @Test
+ public void stopBuildSecurity(){
+ //intentionally blank as a note that security is performed internally in Hudson core.
+ }
+
+ @Test(expected = ServiceRuntimeException.class)
+ public void stopBuildServiceRuntimeException1() throws IOException {
+ BuildService inst = spy(getInst());
+
+ doReturn(build).when(inst).getBuild(project, 1);
+ try {
+ doThrow(new IOException()).when(build).doStop(any(StaplerRequest.class), any(StaplerResponse.class));
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ getInst().stopBuild(project, 1);
+ }
+
+ @Test(expected = ServiceRuntimeException.class)
+ public void stopBuildServiceRuntimeException2() throws IOException {
+ BuildService inst = spy(getInst());
+
+ doReturn(build).when(inst).getBuild(project, 1);
+ try {
+ doThrow(new ServletException()).when(build).doStop(any(StaplerRequest.class), any(StaplerResponse.class));
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ getInst().stopBuild(project, 1);
+ }
+
+
+
+}
diff --git a/hudson-service/src/test/java/org/eclipse/hudson/service/internal/DependencyGraphServiceImplTest.java b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/DependencyGraphServiceImplTest.java
new file mode 100644
index 00000000..9be014c0
--- /dev/null
+++ b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/DependencyGraphServiceImplTest.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+import hudson.model.TaskListener;
+import hudson.model.AbstractBuild;
+
+import org.eclipse.hudson.service.DependencyGraphService;
+import org.eclipse.hudson.service.internal.DependencyGraphServiceImpl;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DependencyGraphServiceImplTest {
+
+ @Mock private TaskListener listener;
+ @Mock private AbstractBuild<?,?> build;
+
+ private DependencyGraphService getInst(){
+ return new DependencyGraphServiceImpl();
+ }
+
+ @Test
+ public void getInstNotNull() {
+ assertThat(getInst(),notNullValue());
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void triggerDependentsNullArg1() {
+ getInst().triggerDependents(null, listener);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void triggerDependentsNullArg2() {
+ getInst().triggerDependents(build, null);
+ }
+
+
+}
diff --git a/hudson-service/src/test/java/org/eclipse/hudson/service/internal/DescriptorServiceImplTest.java b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/DescriptorServiceImplTest.java
new file mode 100644
index 00000000..94657d84
--- /dev/null
+++ b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/DescriptorServiceImplTest.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+
+import org.eclipse.hudson.service.internal.DescriptorServiceImpl;
+import org.junit.Test;
+
+public class DescriptorServiceImplTest {
+
+ private DescriptorServiceImpl getInst() {
+ return new DescriptorServiceImpl();
+ }
+
+ @Test
+ public void getInstNotNull() {
+ assertThat(getInst(),notNullValue());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void getDescriptorNullArg() {
+ getInst().getDescriptor((String) null);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test(expected = NullPointerException.class)
+ public void getDescriptorByTypeNullArg() {
+ getInst().getDescriptor((Class) null);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test(expected = NullPointerException.class)
+ public void getDescriptorOrDieByTypeNullArg() {
+ getInst().getDescriptorOrDie((Class) null);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test(expected = NullPointerException.class)
+ public void getDescriptorByType() {
+ getInst().getDescriptorByType((Class) null);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test(expected = NullPointerException.class)
+ public void getDescriptorList() {
+ getInst().getDescriptorList((Class) null);
+ }
+
+}
diff --git a/hudson-service/src/test/java/org/eclipse/hudson/service/internal/NodeServiceImplTest.java b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/NodeServiceImplTest.java
new file mode 100644
index 00000000..1ac2cc17
--- /dev/null
+++ b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/NodeServiceImplTest.java
@@ -0,0 +1,198 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+import static org.powermock.api.mockito.PowerMockito.*;
+import hudson.model.Hudson;
+import hudson.model.Node;
+import hudson.security.Permission;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.hudson.service.NodeNotFoundException;
+import org.eclipse.hudson.service.NodeService;
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.internal.NodeServiceImpl;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(Hudson.class)
+public class NodeServiceImplTest {
+
+ // setup
+ private Hudson hudson;
+
+ @Mock
+ private SecurityService security;
+
+ @Mock
+ private NodeServiceImpl nodeService;
+
+ @Mock
+ private Node node;
+
+ @Before
+ public void setUp() throws Exception {
+ mockStatic(Hudson.class); // static methods
+ hudson = mock(Hudson.class); // final and native
+
+ MockitoAnnotations.initMocks(this);
+ nodeService = new NodeServiceImpl(security);
+ nodeService.setHudson(hudson);
+ }
+
+ private NodeServiceImpl getInst() {
+ return nodeService;
+ }
+
+ @Test
+ public void setupProperly() {
+ assertThat(getInst(),notNullValue());
+ assertThat(hudson,notNullValue());
+ }
+
+ @Test
+ public void getAllNodesSecurity() {
+
+ List<Node> nodes = new ArrayList<Node>();
+ nodes.add(node);
+ Node masterNode = hudson;
+
+ NodeService inst = spy(getInst());
+
+ doReturn(nodes).when(hudson).getNodes();
+ doReturn(hudson).when(inst).getMasterNode();
+ doReturn(true).when(security).hasPermission(masterNode, Permission.READ);
+
+ List<Node> result = inst.getAllNodes();
+
+ assertThat(result, not(contains(node)));
+ assertThat(result, contains(masterNode));
+ Mockito.verify(security).hasPermission(node, Permission.READ);
+ Mockito.verify(security).hasPermission(masterNode, Permission.READ);
+
+ }
+
+ @Test
+ public void getNodesSecurity() {
+
+ List<Node> nodes = new ArrayList<Node>();
+ nodes.add(node);
+
+ NodeService inst = spy(getInst());
+
+ doReturn(nodes).when(hudson).getNodes();
+ doReturn(true).when(security).hasPermission(node, Permission.READ);
+
+ List<Node> result = inst.getNodes();
+
+ assertThat(result, contains(node));
+
+ Mockito.verify(security).hasPermission(node, Permission.READ);
+
+ }
+
+
+ @Test
+ public void getNodesDoesNotIncludeMaster() {
+
+ List<Node> nodes = new ArrayList<Node>();
+ nodes.add(node);
+ Node masterNode = hudson;
+
+ NodeService inst = spy(getInst());
+
+ doReturn(nodes).when(hudson).getNodes();
+ doReturn(true).when(security).hasPermission(node, Permission.READ);
+ // try to fake it out
+ doReturn(hudson).when(inst).getMasterNode();
+ doReturn(true).when(security).hasPermission(masterNode, Permission.READ);
+
+ List<Node> result = inst.getNodes();
+
+ assertThat(result, contains(node));
+ assertThat(result, not(contains(masterNode)));
+ }
+
+
+
+ @Test(expected=NullPointerException.class)
+ public void getNodeNullArg() {
+ getInst().getNode(null);
+ }
+
+ @Test(expected=NodeNotFoundException.class)
+ public void getNodeNotFound() {
+ getInst().getNode("nodeName");
+ }
+
+ @Test
+ public void getNodeSecurity() {
+ when(hudson.getNode("nodeName")).thenReturn(node);
+ assertThat(getInst().getNode("nodeName"), equalTo(node));
+ Mockito.verify(security).checkPermission(node, Permission.READ);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void findNodeNullArg() {
+ getInst().findNode(null);
+ }
+
+ @Test
+ public void findNodeNotFound() {
+ assertThat(getInst().findNode("nodeName"), nullValue());
+ }
+
+ @Test
+ public void findNodeReturnMaster() {
+ Node masterNode = hudson;
+ assertThat(getInst().findNode(""), equalTo(masterNode));
+ Mockito.verify(hudson, Mockito.never()).getNode("");
+ }
+
+ @Test
+ public void findNodeReturnNotMaster() {
+ when(hudson.getNode("nodeName")).thenReturn(node);
+ assertThat(getInst().findNode("nodeName"), equalTo(node));
+ }
+
+ @Test
+ public void findNodeSecurity() {
+ when(hudson.getNode("nodeName")).thenReturn(node);
+ assertThat(getInst().findNode("nodeName"), equalTo(node));
+ Mockito.verify(security).checkPermission(node, Permission.READ);
+ }
+
+ @Test
+ public void getMasterNodeIsHudson() {
+ Node masterNode = hudson;
+ assertThat(getInst().getMasterNode(), equalTo(masterNode));
+ }
+
+
+
+}
diff --git a/hudson-service/src/test/java/org/eclipse/hudson/service/internal/ProjectServiceImplTest.java b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/ProjectServiceImplTest.java
new file mode 100644
index 00000000..b4f310e7
--- /dev/null
+++ b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/ProjectServiceImplTest.java
@@ -0,0 +1,370 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+import static org.powermock.api.mockito.PowerMockito.*;
+import hudson.model.Item;
+import hudson.model.TopLevelItem;
+import hudson.model.AbstractProject;
+import hudson.model.Hudson;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+import org.eclipse.hudson.service.ProjectNotFoundException;
+import org.eclipse.hudson.service.ProjectService;
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.ServiceRuntimeException;
+import org.eclipse.hudson.service.SystemIntegrityViolationException;
+import org.eclipse.hudson.service.internal.ProjectServiceImpl;
+import org.eclipse.hudson.utils.tasks.JobUuid;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ Hudson.class, JobUuid.class })
+public class ProjectServiceImplTest {
+
+ @Mock
+ private SecurityService securityService;
+
+ @SuppressWarnings("rawtypes")
+ @Mock
+ private AbstractProject project;
+
+ @Mock
+ private InputStream input;
+
+ private Hudson hudson;
+
+ private ProjectServiceImpl projectService;
+
+ @Before
+ public void setUp() throws Exception {
+ mockStatic(Hudson.class);
+ hudson = PowerMockito.mock(Hudson.class);
+ PowerMockito.mockStatic(JobUuid.class);
+
+ MockitoAnnotations.initMocks(this);
+ this.projectService = new ProjectServiceImpl(securityService);
+ this.projectService.setHudson(hudson);
+
+ assertThat(getInst(), notNullValue());
+ }
+
+ private ProjectServiceImpl getInst() {
+ return projectService;
+ }
+
+ // constructors ---------------
+
+ @SuppressWarnings("unused")
+ @Test(expected = NullPointerException.class)
+ public void constructorNullArg() {
+ new ProjectServiceImpl(null);
+ }
+
+ // getProjectByUuid --------------
+
+ @Test(expected = NullPointerException.class)
+ public void getProjectByUUIDNullArg() {
+ getInst().getProject((UUID) null);
+ }
+
+ @Test(expected = ProjectNotFoundException.class)
+ public void getProjectByUUIDNotFound() {
+ ProjectService inst = spy(getInst());
+ UUID id = UUID.randomUUID();
+ doReturn(null).when(inst).findProject(id);
+ inst.getProject(id);
+ }
+
+ // getProjectByFullname ----------------
+
+ @Test(expected = NullPointerException.class)
+ public void getProjectByFullnameNullArg() {
+ getInst().getProjectByFullName(null);
+ }
+
+ @Test(expected = ProjectNotFoundException.class)
+ public void getProjectByFullnameProjectNotFoundException() {
+ getInst().getProjectByFullName("fullname");
+ }
+
+ @Test
+ public void getProjectByFullnameSecurity() {
+ doReturn(project).when(hudson).getItemByFullName("fullname", AbstractProject.class);
+
+ AbstractProject<?, ?> result = getInst().getProjectByFullName("fullname");
+
+ assertThat(result, equalTo(project));
+ Mockito.verify(securityService).checkPermission(project, Item.READ);
+ }
+
+ // getProjectByProjectName ---------
+
+ @Test(expected = ProjectNotFoundException.class)
+ public void getProjectByProjectNameProjectNotFoundException() {
+ getInst().getProject("projectName");
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void getProjectByProjectNameNullArg() {
+ getInst().getProject((String) null);
+ }
+
+ @Test
+ public void getProjectByProjectNameSecurity() {
+ ProjectService inst = spy(getInst());
+
+ doReturn(project).when(hudson).getItemByFullName("projectName", AbstractProject.class);
+ @SuppressWarnings("unchecked")
+ Collection<String> names = Mockito.mock(Collection.class);
+ when(names.contains("projectName")).thenReturn(true);
+ when(inst.getProjectNames()).thenReturn(names);
+
+ AbstractProject<?, ?> result = getInst().getProject("projectName");
+
+ assertThat(result, equalTo(project));
+ Mockito.verify(securityService).checkPermission(project, Item.READ);
+ }
+
+ // findProjectByUUID -------
+
+ @Test(expected = NullPointerException.class)
+ public void findProjectByUUIDNullArg() {
+ getInst().findProject((UUID) null);
+ }
+
+ @Test
+ public void findProjectByUUIDNotFound() {
+ UUID id = UUID.randomUUID();
+ assertThat(getInst().findProject(id), nullValue());
+ }
+
+ // findProjectByProjectName
+
+ @Test(expected = NullPointerException.class)
+ public void findProjectByProjectNameNullArg() {
+ getInst().findProject((String) null);
+ }
+
+ @Test
+ public void findProjectByProjectNameProjectNotFoundException() {
+ assertThat(getInst().findProject("projectName"), nullValue());
+ }
+
+ @Test
+ public void findProjectByProjectNameSecurity() {
+ ProjectService inst = spy(getInst());
+
+ doReturn(project).when(hudson).getItemByFullName("projectName", AbstractProject.class);
+ @SuppressWarnings("unchecked")
+ Collection<String> names = Mockito.mock(Collection.class);
+ when(names.contains("projectName")).thenReturn(true);
+ when(inst.getProjectNames()).thenReturn(names);
+
+ AbstractProject<?, ?> result = getInst().findProject("projectName");
+
+ assertThat(result, equalTo(project));
+ Mockito.verify(securityService).checkPermission(project, Item.READ);
+ }
+
+ // findProjectByFullname ------------
+
+ @Test(expected = NullPointerException.class)
+ public void findProjectByFullnameNullArg() {
+ getInst().findProjectByFullName(null);
+ }
+
+ @Test
+ public void findProjectByFullnameNotFound() {
+ assertThat(getInst().findProjectByFullName("fullname"), nullValue());
+ }
+
+ @Test
+ public void findProjectByFullnameSecurity() {
+ doReturn(project).when(hudson).getItemByFullName("fullname", AbstractProject.class);
+
+ AbstractProject<?, ?> result = getInst().findProjectByFullName("fullname");
+
+ assertThat(result, equalTo(project));
+ Mockito.verify(securityService).checkPermission(project, Item.READ);
+ }
+
+ // getAllProjects -----------
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test
+ public void getAllProjectsSecurity() {
+ @SuppressWarnings("unchecked")
+ List<AbstractProject> projects = Mockito.mock(List.class);
+ doReturn(projects).when(hudson).getAllItems(AbstractProject.class);
+
+ List<AbstractProject> result = getInst().getAllProjects();
+
+ assertThat(result, equalTo(projects));
+
+ // Hudson getAllItems has permission check.
+ Mockito.verify(hudson).getAllItems(Mockito.isA(Class.class));
+ }
+
+ // copyProject -----------
+ @Test(expected = NullPointerException.class)
+ public void copyProjectNullArg1() {
+ getInst().copyProject(null, "toProject");
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void copyProjectNullArg2() {
+ getInst().copyProject(project, null);
+ }
+
+ @Test
+ public void copyProjectSecurity() throws IOException {
+
+ // mocks
+ AbstractProject<?, ?> toProject = Mockito.mock(AbstractProject.class);
+ ProjectService inst = spy(getInst());
+
+ // make method succeed
+ doReturn(false).when(inst).projectExists("toProject");
+ when(hudson.copy(project, "toProject")).thenReturn(toProject);
+
+ // test
+ inst.copyProject(project, "toProject");
+
+ // verify
+ Mockito.verify(securityService).checkPermission(Item.CREATE);
+ Mockito.verify(securityService).checkPermission(project, Item.EXTENDED_READ);
+
+ }
+
+ @Test(expected = SystemIntegrityViolationException.class)
+ public void copyProjectToProjectAlreadyExists() throws IOException {
+ ProjectService inst = spy(getInst());
+ doReturn(true).when(inst).projectExists("toProject");
+ inst.copyProject(project, "toProject");
+ }
+
+ @Test(expected = ServiceRuntimeException.class)
+ public void copyProjectIOException() throws IOException {
+
+ // mocks
+ ProjectService inst = spy(getInst());
+
+ // make method succeed
+ doReturn(false).when(inst).projectExists("toProject");
+ when(hudson.copy(project, "toProject")).thenThrow(new IOException());
+
+ // test
+ inst.copyProject(project, "toProject");
+
+ }
+
+ // createProjectfromXml ------
+
+ @Test(expected = NullPointerException.class)
+ public void createProjectFromXMLNullArg1() {
+ getInst().createProjectFromXML(null, input);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void createProjectFromXMLNullArg2() {
+ getInst().createProjectFromXML("toProject", null);
+ }
+
+ @Test
+ public void createProjectFromXMLSecurity() throws IOException {
+ // mocks
+ TopLevelItem toProject = Mockito.mock(TopLevelItem.class);
+ ProjectService inst = spy(getInst());
+
+ // make method succeed
+ doReturn(false).when(inst).projectExists("toProject");
+ when(hudson.createProjectFromXML("toProject", input)).thenReturn(toProject);
+
+ assertThat(inst.createProjectFromXML("toProject", input), equalTo(toProject));
+
+ Mockito.verify(securityService).checkPermission(Item.CREATE);
+ }
+
+ @Test(expected = SystemIntegrityViolationException.class)
+ public void createProjectFromXMLToProjectAlreadyExists() throws IOException {
+ ProjectService inst = spy(getInst());
+ doReturn(true).when(inst).projectExists("toProject");
+ inst.createProjectFromXML("toProject", input);
+ }
+
+ @Test(expected = ServiceRuntimeException.class)
+ public void createProjectFromXMLIOException() throws IOException {
+
+ // mocks
+ ProjectService inst = spy(getInst());
+
+ // make method succeed
+ doReturn(false).when(inst).projectExists("toProject");
+ when(hudson.createProjectFromXML("toProject", input)).thenThrow(new IOException());
+
+ // test
+ inst.createProjectFromXML("toProject", input);
+
+ }
+
+ // getProjectNames -----------
+ @Test
+ public void getProjectNamesSecurity() {
+ // blank, security Item.READ check performed by Hudson internals
+ }
+
+ // projectExists ------------
+
+ @Test
+ public void projectExistsSecurity() {
+ // blank, security Item.READ check performed by Hudson internals
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void projectExistsNullArg() {
+ getInst().projectExists(null);
+ }
+
+ @Test
+ public void projectExistsTrue() {
+ ProjectService inst = spy(getInst());
+
+ @SuppressWarnings("unchecked")
+ Collection<String> names = Mockito.mock(Collection.class);
+ when(names.contains("projectName")).thenReturn(true);
+ when(inst.getProjectNames()).thenReturn(names);
+
+ assertThat(inst.projectExists("projectName"), is(true));
+ }
+
+}
diff --git a/hudson-service/src/test/java/org/eclipse/hudson/service/internal/QueueServiceImplTest.java b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/QueueServiceImplTest.java
new file mode 100644
index 00000000..087d4c4f
--- /dev/null
+++ b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/QueueServiceImplTest.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+import static org.powermock.api.mockito.PowerMockito.*;
+import hudson.model.Hudson;
+
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.internal.QueueServiceImpl;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ Hudson.class})
+public class QueueServiceImplTest {
+
+ private Hudson hudson;
+
+ private QueueServiceImpl queueService;
+
+ @Mock
+ private SecurityService security;
+
+ @Before
+ public void setUp() throws Exception {
+ mockStatic(Hudson.class); // static methods
+ hudson = mock(Hudson.class); // final and native
+
+ MockitoAnnotations.initMocks(this);
+ queueService = new QueueServiceImpl(security);
+ queueService.setHudson(hudson);
+ }
+
+ private QueueServiceImpl getInst() {
+ return queueService;
+ }
+
+ @Test
+ public void setupProperly() {
+ assertThat(getInst(),notNullValue());
+ assertThat(hudson,notNullValue());
+ }
+
+ @Test
+ public void getQueueSecurity() {
+ getInst().getQueue();
+ Mockito.verify(security).checkPermission(Hudson.ADMINISTER);
+ }
+
+}
diff --git a/hudson-service/src/test/java/org/eclipse/hudson/service/internal/ScriptServiceImplTest.java b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/ScriptServiceImplTest.java
new file mode 100644
index 00000000..af95934d
--- /dev/null
+++ b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/ScriptServiceImplTest.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+import static org.powermock.api.mockito.PowerMockito.*;
+import hudson.model.Hudson;
+import hudson.model.Hudson.MasterComputer;
+import hudson.util.RemotingDiagnostics;
+
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.internal.ScriptServiceImpl;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ Hudson.class, MasterComputer.class, RemotingDiagnostics.class })
+public class ScriptServiceImplTest {
+
+ private Hudson hudson;
+
+ private ScriptServiceImpl scriptService;
+
+ @Mock
+ private SecurityService security;
+
+ @Before
+ public void setUp() throws Exception {
+ mockStatic(Hudson.class); // static methods
+ hudson = mock(Hudson.class); // final and native
+
+ mockStatic(RemotingDiagnostics.class);
+ mockStatic(MasterComputer.class);
+
+ MockitoAnnotations.initMocks(this);
+ scriptService = new ScriptServiceImpl(security);
+ scriptService.setHudson(hudson);
+ }
+
+ private ScriptServiceImpl getInst() {
+ return scriptService;
+ }
+
+ @Test
+ public void setupProperly() {
+ assertThat(getInst(), notNullValue());
+ assertThat(hudson, notNullValue());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void executeArgNull() throws Exception {
+ getInst().execute(null);
+ }
+
+ @Test
+ public void executeSecurity() throws Exception {
+ getInst().execute("String bogus");
+ Mockito.verify(security).checkPermission(Hudson.ADMINISTER);
+ }
+
+}
diff --git a/hudson-service/src/test/java/org/eclipse/hudson/service/internal/SecurityServiceImplTest.java b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/SecurityServiceImplTest.java
new file mode 100644
index 00000000..67d1319f
--- /dev/null
+++ b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/SecurityServiceImplTest.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+import hudson.security.AccessControlled;
+import hudson.security.Permission;
+
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.internal.SecurityServiceImpl;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SecurityServiceImplTest {
+
+ @Mock private AccessControlled ac;
+
+ private SecurityService securityService;
+
+ @Before
+ public void setup(){
+ securityService = new SecurityServiceImpl();
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void checkPermissionNullArg1(){
+ assertNotNull(securityService);
+ securityService.checkPermission(null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void checkPermissionAccessControlledNullArg1(){
+ assertNotNull(securityService);
+ securityService.checkPermission(null, Permission.DELETE);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void checkPermissionAccessControlledNullArg2(){
+ assertNotNull(securityService);
+ assertNotNull(ac);
+ securityService.checkPermission(ac, null);
+ }
+
+ @Test
+ public void checkPermissionAccessControlled(){
+ securityService.checkPermission(ac, Permission.DELETE);
+ verify(ac).checkPermission(Permission.DELETE);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void hasPermissionAccessControlledNullArg1(){
+ assertNotNull(securityService);
+ securityService.hasPermission(null, Permission.DELETE);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void hasPermissionAccessControlledNullArg2(){
+ assertNotNull(securityService);
+ assertNotNull(ac);
+ securityService.hasPermission(ac, null);
+ }
+
+ @Test
+ public void hasPermissionAccessControlled(){
+ securityService.hasPermission(ac, Permission.DELETE);
+ verify(ac).hasPermission(Permission.DELETE);
+ }
+
+
+}
diff --git a/hudson-service/src/test/java/org/eclipse/hudson/service/internal/SystemServiceImplTest.java b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/SystemServiceImplTest.java
new file mode 100644
index 00000000..4d6274f2
--- /dev/null
+++ b/hudson-service/src/test/java/org/eclipse/hudson/service/internal/SystemServiceImplTest.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2010-2011 Sonatype, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ *
+ *
+ *
+ *******************************************************************************/
+
+package org.eclipse.hudson.service.internal;
+
+import hudson.model.Hudson;
+
+import org.eclipse.hudson.service.SecurityService;
+import org.eclipse.hudson.service.SystemService;
+import org.eclipse.hudson.service.internal.SystemServiceImpl;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.io.File;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+/**
+ * Tests for {@link SystemServiceImpl}.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(Hudson.class)
+public class SystemServiceImplTest
+{
+
+ private Hudson hudson;
+
+ private SystemServiceImpl system;
+
+ @Before
+ public void setUp() throws Exception {
+ mockStatic(Hudson.class);
+ hudson = mock(Hudson.class);
+
+ SecurityService security = mock(SecurityService.class);
+ system = new SystemServiceImpl(security);
+ system.setHudson(hudson);
+ }
+
+ @Test
+ public void getUrlReturnsValidWhenHudsonInstanceExists() {
+ assertEquals(SystemService.DEFAULT_URL, system.getUrl());
+ }
+
+ @Test
+ public void getUrlFromHudson() {
+ when(system.getUrl()).thenReturn("http://www.foobar.com:8070");
+ assertEquals("http://www.foobar.com:8070", system.getUrl());
+ }
+
+ @Test
+ public void getUrlFromHudsonWithSlashAtEnd() {
+ when(system.getUrl()).thenReturn("http://www.foobar.com:8070/");
+ assertEquals("http://www.foobar.com:8070", system.getUrl());
+ }
+
+ /**
+ * Default Url should not end with slash, as if it did getUrl would do extra processing
+ */
+ @Test
+ public void verifyDefaultUrlDoesNotEndWithSlash() {
+ assertFalse(SystemService.DEFAULT_URL.endsWith("/"));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void getInstallationDirectoryThrowsIllegalStateException() {
+ system.getInstallationDirectory();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void getLogDirectoryThrowsIllegalStateException() {
+ system.getLogDirectory();
+ }
+
+ @Test
+ public void getWorkingDirectory() {
+ File file = new File("/foo/biz/bat");
+ when(hudson.getRootDir()).thenReturn(file);
+ assertEquals(file, system.getWorkingDirectory());
+ }
+
+}

Back to the top