View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.maven.nexus;
17  
18  import java.io.IOException;
19  
20  import org.apache.commons.httpclient.HttpClient;
21  import org.apache.commons.httpclient.HttpException;
22  import org.apache.commons.httpclient.HttpMethod;
23  import org.apache.commons.httpclient.NameValuePair;
24  import org.apache.commons.httpclient.methods.GetMethod;
25  import org.apache.maven.plugin.logging.Log;
26  import org.jdom.Document;
27  import org.jdom.Text;
28  import org.jdom.input.SAXBuilder;
29  import org.jdom.xpath.XPath;
30  import org.xml.sax.InputSource;
31  
32  import de.smartics.maven.issue.command.CommandException;
33  import de.smartics.maven.issue.command.CommandResult.HttpStatus;
34  import de.smartics.maven.issue.util.HttpClientUtils;
35  import de.smartics.maven.issue.util.LogLevel;
36  import de.smartics.util.lang.Arg;
37  
38  /**
39   * Runs a REST command against a Nexus server to fetch artifact resolution
40   * information.
41   */
42  public final class NexusClient
43  {
44    // ********************************* Fields *********************************
45  
46    // --- constants ------------------------------------------------------------
47  
48    /**
49     * The minimum HTTP code signaling a successful request.
50     */
51    private static final int MIN_OK = 200;
52  
53    /**
54     * The maximum HTTP code signaling a successful request.
55     */
56    private static final int MAX_OK = 299;
57  
58    /**
59     * The status code for a HTTP 404.
60     */
61    private static final int NOT_FOUND = 404;
62  
63    // --- members --------------------------------------------------------------
64  
65    /**
66     * The client to issue requests to the Nexus server.
67     */
68    private final HttpClient client;
69  
70    /**
71     * The logger to log to.
72     */
73    private final Log log;
74  
75    /**
76     * Defines the verboseness of the output.
77     */
78    private final LogLevel logLevel;
79  
80    // ****************************** Initializer *******************************
81  
82    // ****************************** Constructors ******************************
83  
84    /**
85     * Default constructor.
86     *
87     * @param client the client to issue requests to the Nexus server.
88     * @param log the logger to log to.
89     * @param logLevel the value for logLevel.
90     * @throws NullPointerException if {@code client} is <code>null</code>.
91     * @throws IllegalArgumentException if {@code client} is blank.
92     */
93    public NexusClient(final HttpClient client, final Log log,
94        final LogLevel logLevel) throws NullPointerException,
95      IllegalArgumentException
96    {
97      this.client = Arg.checkNotNull("client", client);
98      this.log = log;
99      this.logLevel = logLevel;
100   }
101 
102   // ****************************** Inner Classes *****************************
103 
104   // ********************************* Methods ********************************
105 
106   // --- init -----------------------------------------------------------------
107 
108   // --- get&set --------------------------------------------------------------
109 
110   // --- business -------------------------------------------------------------
111 
112   /**
113    * Retrieves the latest version from the Nexus server.
114    *
115    * @param request the request parameters.
116    * @return the latest version as reported by Nexus.
117    * @throws CommandException on any problem requesting Nexus.
118    */
119   public String getLatestReleaseVersion(final NexusRequest request)
120     throws CommandException
121   {
122     if (logLevel.isVerbose())
123     {
124       log.info("Fetching latest version from Nexus: " + request);
125     }
126 
127     final HttpMethod method = createMethod(request);
128     if (logLevel.isVerbose())
129     {
130       log.info("Executing method: " + toString(method));
131     }
132     execute(method);
133 
134     final HttpStatus httpStatus = createStatus(method);
135 
136     final int httpStatusCode = httpStatus.getCode();
137     if (httpStatusCode < MIN_OK || httpStatusCode > MAX_OK)
138     {
139       final String message =
140           "Failed to execute command '" + toString(method) + "': " + httpStatus;
141       if (httpStatusCode == NOT_FOUND)
142       {
143         log.info("Cannot find artifact '" + request.getArtifactString()
144                  + "' to resolve latest release version on nexus.");
145       }
146       else
147       {
148         if (logLevel.isVerbose())
149         {
150           log.info(message);
151         }
152       }
153 
154       throw new CommandException(message);
155     }
156 
157     final String version = readVersion(method);
158     return version;
159   }
160 
161   private static String toString(final HttpMethod method)
162   {
163     return method.getPath() + '?' + method.getQueryString();
164   }
165 
166   private String readVersion(final HttpMethod method)
167   {
168     final InputSource bodyContent =
169         HttpClientUtils.readBodyContentAsStream(method);
170 
171     final SAXBuilder builder = new SAXBuilder();
172     try
173     {
174       final Document responseXml = builder.build(bodyContent);
175       final String xPathString = "artifact-resolution/data/version/text()";
176       final Text versionNode =
177           (Text) XPath.selectSingleNode(responseXml, xPathString);
178       if (versionNode != null)
179       {
180         return versionNode.getTextNormalize();
181       }
182       else
183       {
184         throw new CommandException("No version found in Nexus response.");
185       }
186     }
187     catch (final Exception e)
188     {
189       throw new CommandException("Failed to parse Nexus response.", e);
190     }
191   }
192 
193   private int execute(final HttpMethod method) throws CommandException
194   {
195     try
196     {
197       method.getParams().setContentCharset("UTF-8");
198       final int code = client.executeMethod(method);
199       return code;
200     }
201     catch (final HttpException e)
202     {
203       throw new CommandException("Cannot execute command '" + method + "'.", e);
204     }
205     catch (final IOException e)
206     {
207       throw new CommandException("Cannot execute command '" + method + "'.", e);
208     }
209   }
210 
211   private HttpStatus createStatus(final HttpMethod method)
212   {
213     final int code = method.getStatusCode();
214     final String text = method.getStatusText();
215     final HttpStatus httpStatus = new HttpStatus(code, text);
216     return httpStatus;
217   }
218 
219   private HttpMethod createMethod(final NexusRequest request)
220   {
221     final String url = createUrl(request);
222     final GetMethod method = new GetMethod(url);
223 
224     final NameValuePair[] params =
225         new NameValuePair[] {
226                              new NameValuePair("r", request.getRepositoryId()),
227                              new NameValuePair("g", request.getGroupId()),
228                              new NameValuePair("a", request.getArtifactId()),
229                              new NameValuePair("e", request.getExtension()),
230                              new NameValuePair("v", "RELEASE") };
231     method.setQueryString(params);
232 
233     return method;
234   }
235 
236   private String createUrl(final NexusRequest request)
237   {
238     final StringBuilder buffer = new StringBuilder(64);
239     buffer.append(request.getNexusServer()).append(
240         "/service/local/artifact/maven/resolve");
241     return buffer.toString();
242   }
243 
244   // --- object basics --------------------------------------------------------
245 
246 }