com.marvelution.hudson.plugins.apiv2.APIv2Plugin Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hudson-apiv2-plugin Show documentation
Show all versions of hudson-apiv2-plugin Show documentation
This plugin features a Wink REST API application.
The newest version!
/*
* Licensed to Marvelution under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Marvelution licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.marvelution.hudson.plugins.apiv2;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.servlet.Filter;
import javax.servlet.ServletException;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import com.google.common.collect.Lists;
import com.marvelution.hudson.plugins.apiv2.cache.activity.ActivitiesCache;
import com.marvelution.hudson.plugins.apiv2.cache.activity.BuildActivityCache;
import com.marvelution.hudson.plugins.apiv2.cache.activity.JobActivityCache;
import com.marvelution.hudson.plugins.apiv2.cache.issue.IssueCache;
import com.marvelution.hudson.plugins.apiv2.cache.issue.IssueKey;
import com.marvelution.hudson.plugins.apiv2.cache.issue.IssuesCache;
import com.marvelution.hudson.plugins.apiv2.servlet.filter.HudsonAPIV2ServletFilter;
import com.marvelution.hudson.plugins.apiv2.threads.IssueIndexingThread;
import com.marvelution.hudson.plugins.apiv2.utils.JiraKeyUtils;
import com.thoughtworks.xstream.XStream;
import hudson.Plugin;
import hudson.model.Hudson;
import hudson.model.Descriptor.FormException;
import hudson.util.CopyOnWriteList;
import hudson.util.FormValidation;
import hudson.util.PluginServletFilter;
/**
* Main Plugin implementation of the Hudson REST plugin.
* This class is responsible for the registration of the
* Wink Application implementation
*
* @author Mark Rekveld
*/
public class APIv2Plugin extends Plugin {
private static final Logger LOGGER = Logger.getLogger(APIv2Plugin.class.getName());
private static final XStream XSTREAM = new XStream();
private static final String APIV2_DIRECTORY_NAME = "APIv2";
private static final String ACTIVITIES_CACHE_FILE = "activities-cache.xml";
private static final String ISSUES_CACHE_FILE = "issues-cache.xml";
private static final String APIV2_ISSUE_KEY_PATTERN = "apiv2.issue.key.pattern";
private static final String APIV2_PATTERN_KEY = "apiv2.pattern";
private static APIv2Plugin plugin;
private transient List filters = Lists.newArrayList();
private transient ActivitiesCache activitiesCache = new ActivitiesCache();
private transient IssuesCache issuesCache = new IssuesCache();
private final CopyOnWriteList patterns = new CopyOnWriteList();
/**
* {@inheritDoc}
*/
@Override
public void start() throws Exception {
super.start();
plugin = this;
load();
LOGGER.info("Adding the APIv2 Filters");
filters.add(new HudsonAPIV2ServletFilter());
for (Filter filter : filters) {
PluginServletFilter.addFilter(filter);
}
LOGGER.info("Loading the Activity Cache");
File activityCacheFile = getFile(ACTIVITIES_CACHE_FILE);
if (activityCacheFile.exists()) {
try {
activitiesCache = (ActivitiesCache) XSTREAM.fromXML(new FileInputStream(activityCacheFile));
} catch (Exception e) {
activitiesCache = new ActivitiesCache();
LOGGER.severe("Failed to load the " + ACTIVITIES_CACHE_FILE
+ " and will thus not be available for the REST APIs");
}
}
LOGGER.info("Loading the Issues Cache");
File issuesCacheFile = getFile(ISSUES_CACHE_FILE);
if (issuesCacheFile.exists()) {
try {
issuesCache = (IssuesCache) XSTREAM.fromXML(new FileInputStream(issuesCacheFile));
} catch (Exception e) {
issuesCache = new IssuesCache();
LOGGER.severe("Failed to load the " + ISSUES_CACHE_FILE
+ " and will thus not be available for the REST APIs");
}
}
if (StringUtils.isBlank(issuesCache.getIssueKeyRegex())) {
// Make sure the pattern is set
issuesCache.setIssueKeyRegex(JiraKeyUtils.DEFAULT_JIRA_ISSUE_KEY_PATTERN.pattern());
}
}
/**
* {@inheritDoc}
*/
@Override
public void stop() throws Exception {
super.stop();
LOGGER.info("Removing the APIv2 Filters");
for (Filter filter : filters) {
PluginServletFilter.removeFilter(filter);
}
filters.clear();
LOGGER.info("Storing the Activity Cache");
XSTREAM.toXML(activitiesCache, new FileOutputStream(getFile(ACTIVITIES_CACHE_FILE)));
LOGGER.info("Storing the Issue Cache");
XSTREAM.toXML(issuesCache, new FileOutputStream(getFile(ISSUES_CACHE_FILE)));
save();
plugin = null;
}
/**
* {@inheritDoc}
*/
@Override
public void configure(StaplerRequest req, JSONObject formData) throws IOException, ServletException,
FormException {
if (req.hasParameter(APIV2_ISSUE_KEY_PATTERN)
&& StringUtils.isNotBlank(req.getParameter(APIV2_ISSUE_KEY_PATTERN))) {
issuesCache.setIssueKeyRegex(req.getParameter(APIV2_ISSUE_KEY_PATTERN));
IssueIndexingThread.invoke();
}
String[] newPatterns = req.getParameterValues(APIV2_PATTERN_KEY);
if (newPatterns != null && !Arrays.equals(newPatterns, patterns.toArray(new String[patterns.size()]))) {
// TODO Also trigger a full index rescan
patterns.replaceBy(newPatterns);
save();
}
}
/**
* Web Method to validate a given {@link Pattern}
*
* @param value the {@link Pattern} to validate
* @return validation result
* @throws IOException in case of errors
* @throws ServletException in case of errors
*/
public FormValidation doCheckPattern(@QueryParameter final String value) throws IOException, ServletException {
if (!Hudson.getInstance().hasPermission(Hudson.ADMINISTER) || StringUtils.isBlank(value)) {
return FormValidation.ok();
}
try {
Pattern.compile(value);
return FormValidation.ok();
} catch (PatternSyntaxException e) {
StringBuilder builder = new StringBuilder(e.getDescription());
if (e.getIndex() >= 0) {
builder.append(" near index ").append(e.getIndex());
}
builder.append("");
builder.append(e.getPattern()).append(System.getProperty("line.separator"));
if (e.getIndex() >= 0) {
for (int i = 0; i < e.getIndex(); ++i) {
builder.append(' ');
}
builder.append('^');
}
builder.append("
");
return FormValidation.errorWithMarkup(builder.toString());
}
}
/**
* Getter for the plugin instance
*
* @return the {@link APIv2Plugin} plugin instance
*/
public static APIv2Plugin getPlugin() {
return plugin;
}
/**
* Getter for patterns
*
* @return the patterns
*/
public String[] getPatterns() {
return patterns.toArray(new String[patterns.size()]);
}
/**
* Getter for the pattern used to index issue key
*
* @return the Issue Key pattern
* @since 4.5.0
*/
public String getIssueKeyPattern() {
return issuesCache.getIssueKeyRegex();
}
/**
* Getter for the {@link ActivitiesCache}
*
* @return the {@link ActivitiesCache}
*/
public static ActivitiesCache getActivitiesCache() {
return getPlugin().activitiesCache;
}
/**
* Getter for the {@link IssuesCache}
*
* @return the {@link IssuesCache}
*/
public static IssuesCache getIssuesCache() {
return getPlugin().issuesCache;
}
/**
* Getter for a {@link File} by name
*
* @param filename the file name to get
* @return the {@link File}
*/
public File getFile(String filename) {
File dir = new File(getConfigXml().getFile().getParent(), APIV2_DIRECTORY_NAME);
if (!dir.exists()) {
dir.mkdirs();
}
return new File(dir, filename);
}
/**
* Initialize the {@link XStream} object
*/
static {
XSTREAM.autodetectAnnotations(true);
XSTREAM.processAnnotations(ActivitiesCache.class);
XSTREAM.processAnnotations(JobActivityCache.class);
XSTREAM.processAnnotations(BuildActivityCache.class);
XSTREAM.processAnnotations(IssuesCache.class);
XSTREAM.processAnnotations(IssueCache.class);
XSTREAM.processAnnotations(IssueKey.class);
}
}