View Javadoc

1   /*
2    * Copyright 2012 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.Arguments;
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     */
91    public NexusClient(final HttpClient client, final Log log,
92        final LogLevel logLevel)
93    {
94      Arguments.checkNotNull("client", client);
95  
96      this.client = client;
97      this.log = log;
98      this.logLevel = logLevel;
99    }
100 
101   // ****************************** Inner Classes *****************************
102 
103   // ********************************* Methods ********************************
104 
105   // --- init -----------------------------------------------------------------
106 
107   // --- get&set --------------------------------------------------------------
108 
109   // --- business -------------------------------------------------------------
110 
111   /**
112    * Retrieves the latest version from the Nexus server.
113    *
114    * @param request the request parameters.
115    * @return the latest version as reported by Nexus.
116    * @throws CommandException on any problem requesting Nexus.
117    */
118   public String getLatestReleaseVersion(final NexusRequest request)
119     throws CommandException
120   {
121     if (logLevel.isVerbose())
122     {
123       log.info("Fetching latest version from Nexus: " + request);
124     }
125 
126     final HttpMethod method = createMethod(request);
127     if (logLevel.isVerbose())
128     {
129       log.info("Executing method: " + toString(method));
130     }
131     execute(method);
132 
133     final HttpStatus httpStatus = createStatus(method);
134 
135     final int httpStatusCode = httpStatus.getCode();
136     if (httpStatusCode < MIN_OK || httpStatusCode > MAX_OK)
137     {
138       final String message =
139           "Failed to execute command '" + toString(method) + "': " + httpStatus;
140       if (httpStatusCode == NOT_FOUND)
141       {
142         log.info("Cannot find artifact '" + request.getArtifactString()
143                  + "' to resolve latest release version on nexus.");
144       }
145       else
146       {
147         if (logLevel.isVerbose())
148         {
149           log.info(message);
150         }
151       }
152 
153       throw new CommandException(message);
154     }
155 
156     final String version = readVersion(method);
157     return version;
158   }
159 
160   private static String toString(final HttpMethod method)
161   {
162     return method.getPath() + '?' + method.getQueryString();
163   }
164 
165   private String readVersion(final HttpMethod method)
166   {
167     final InputSource bodyContent =
168         HttpClientUtils.readBodyContentAsStream(method);
169 
170     final SAXBuilder builder = new SAXBuilder();
171     try
172     {
173       final Document responseXml = builder.build(bodyContent);
174       final String xPathString = "artifact-resolution/data/version/text()";
175       final Text versionNode =
176           (Text) XPath.selectSingleNode(responseXml, xPathString);
177       if (versionNode != null)
178       {
179         return versionNode.getTextNormalize();
180       }
181       else
182       {
183         throw new CommandException("No version found in Nexus response.");
184       }
185     }
186     catch (final Exception e)
187     {
188       throw new CommandException("Failed to parse Nexus response.", e);
189     }
190   }
191 
192   private int execute(final HttpMethod method) throws CommandException
193   {
194     try
195     {
196       method.getParams().setContentCharset("UTF-8");
197       final int code = client.executeMethod(method);
198       return code;
199     }
200     catch (final HttpException e)
201     {
202       throw new CommandException("Cannot execute command '" + method + "'.", e);
203     }
204     catch (final IOException e)
205     {
206       throw new CommandException("Cannot execute command '" + method + "'.", e);
207     }
208   }
209 
210   private HttpStatus createStatus(final HttpMethod method)
211   {
212     final int code = method.getStatusCode();
213     final String text = method.getStatusText();
214     final HttpStatus httpStatus = new HttpStatus(code, text);
215     return httpStatus;
216   }
217 
218   private HttpMethod createMethod(final NexusRequest request)
219   {
220     final String url = createUrl(request);
221     final GetMethod method = new GetMethod(url);
222 
223     final NameValuePair[] params =
224         new NameValuePair[] {
225                              new NameValuePair("r", request.getRepositoryId()),
226                              new NameValuePair("g", request.getGroupId()),
227                              new NameValuePair("a", request.getArtifactId()),
228                              new NameValuePair("v", "RELEASE") };
229     method.setQueryString(params);
230 
231     return method;
232   }
233 
234   private String createUrl(final NexusRequest request)
235   {
236     final StringBuilder buffer = new StringBuilder(64);
237     buffer.append(request.getNexusServer()).append(
238         "/service/local/artifact/maven/resolve");
239     return buffer.toString();
240   }
241 
242   // --- object basics --------------------------------------------------------
243 
244 }