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.issue.command; 17 18 import java.io.Serializable; 19 import java.util.List; 20 21 import org.apache.commons.httpclient.HttpMethod; 22 import org.apache.commons.httpclient.methods.PostMethod; 23 24 import de.smartics.maven.issue.command.CommandResult.HttpStatus; 25 import de.smartics.maven.issue.command.CommandResult.Page; 26 import de.smartics.maven.issue.util.Grabber; 27 import de.smartics.maven.issue.util.HttpClientUtils; 28 29 /** 30 * Base implementation of the {@link Command} interface. 31 * 32 * @param <T> the concrete type of the command. 33 */ 34 public abstract class AbstractCommand<T extends Command<T>> implements 35 Command<T> 36 { 37 // ********************************* Fields ********************************* 38 39 // --- constants ------------------------------------------------------------ 40 41 /** 42 * The class version identifier. 43 * <p> 44 * The value of this constant is {@value}. 45 * </p> 46 */ 47 private static final long serialVersionUID = 1L; 48 49 // --- members -------------------------------------------------------------- 50 51 /** 52 * The name of the service on the target server. 53 * 54 * @serial 55 */ 56 protected final String service; 57 58 /** 59 * The arguments to the command. 60 * 61 * @serial 62 */ 63 protected final List<CommandArgument<T>> arguments; 64 65 /** 66 * The result of the command execution. May be <code>null</code> if the 67 * command has not been executed yet or if the execution of the command failed 68 * without a result. 69 * 70 * @serial 71 */ 72 protected CommandResult<T> result; 73 74 /** 75 * The URL to the service that has been called. The value is <code>null</code> 76 * if the command was not yet executed. 77 * 78 * @serial 79 */ 80 protected String url; 81 82 // ****************************** Initializer ******************************* 83 84 // ****************************** Constructors ****************************** 85 86 /** 87 * Default constructor. 88 * 89 * @param service the name of the service on the target server. 90 * @param arguments the arguments to the command. 91 */ 92 protected AbstractCommand(final String service, 93 final List<CommandArgument<T>> arguments) 94 { 95 this.service = service; 96 this.arguments = arguments; 97 } 98 99 // ****************************** Inner Classes ***************************** 100 101 /** 102 * Contains information about the expectations and how they have been met. 103 */ 104 public static final class Expectation implements Serializable 105 { 106 // ******************************** Fields ******************************** 107 108 // --- constants ---------------------------------------------------------- 109 110 /** 111 * The class version identifier. 112 * <p> 113 * The value of this constant is {@value}. 114 * </p> 115 */ 116 private static final long serialVersionUID = 1L; 117 118 /** 119 * Constant for commands that do not check expectations. 120 */ 121 public static final Expectation NO_EXPECTATION = new Expectation(true, 122 null, null); 123 124 /** 125 * Signals that the result is not known to the command. Therefore it is a 126 * kind of unexpected result. 127 * <p> 128 * The value of this constant is {@value}. 129 * </p> 130 */ 131 public static final String UNKNOWN_FLAG = "UNEXPECTED"; 132 133 // --- members ------------------------------------------------------------ 134 135 /** 136 * The flag informs about the fact that the expectations have been met ( 137 * <code>true</code>) or not (<code>false</code>). 138 * 139 * @serial 140 */ 141 private final boolean expected; 142 143 /** 144 * The expected page title. 145 * 146 * @serial 147 */ 148 private final String expectedPageTitle; 149 150 /** 151 * The expectation flag that signals the effective result that has been 152 * evaluated on the check if the result is as expected. If there is more 153 * than one expected result (e.g. to add a component may result in the 154 * component being added, recognizing that the component was already added 155 * or in some kind of failure) the result of the analysis is stored here for 156 * further reference by the command itself. 157 * 158 * @serial. 159 */ 160 private final String flag; 161 162 // ***************************** Initializer ****************************** 163 164 // ***************************** Constructors ***************************** 165 166 /** 167 * Default constructor. 168 * 169 * @param expected the flag informs about the fact that the expectations 170 * have been met ( <code>true</code>) or not (<code>false</code>). 171 * @param expectedPageTitle the expected page title. 172 * @param flag the expectation flag that signals the effective result that 173 * has been evaluated on the check if the result is as expected. 174 */ 175 public Expectation(final boolean expected, final String expectedPageTitle, 176 final String flag) 177 { 178 this.expected = expected; 179 this.expectedPageTitle = expectedPageTitle; 180 this.flag = flag; 181 } 182 183 // ***************************** Inner Classes **************************** 184 185 // ******************************** Methods ******************************* 186 187 // --- init --------------------------------------------------------------- 188 189 // --- get&set ------------------------------------------------------------ 190 191 /** 192 * Returns the flag informs about the fact that the expectations have been 193 * met ( <code>true</code>) or not (<code>false</code>). 194 * 195 * @return the flag informs about the fact that the expectations have been 196 * met ( <code>true</code>) or not (<code>false</code>). 197 */ 198 public boolean isExpected() 199 { 200 return expected; 201 } 202 203 /** 204 * Returns the expected page title. 205 * 206 * @return the expected page title. 207 */ 208 public String getExpectedPageTitle() 209 { 210 return expectedPageTitle; 211 } 212 213 /** 214 * Returns the expectation flag that signals the effective result that has 215 * been evaluated on the check if the result is as expected. If there is 216 * more than one expected result (e.g. to add a component may result in the 217 * component being added, recognizing that the component was already added 218 * or in some kind of failure) the result of the analysis is stored here for 219 * further reference by the command itself. 220 * 221 * @return the expectation flag that signals the effective result that has 222 * been evaluated on the check if the result is as expected. 223 */ 224 public String getFlag() 225 { 226 return flag; 227 } 228 229 // --- business ----------------------------------------------------------- 230 231 // --- object basics ------------------------------------------------------ 232 } 233 234 // ********************************* Methods ******************************** 235 236 // --- init ----------------------------------------------------------------- 237 238 /** 239 * Helper to construct an URL to call a service on the target. 240 * 241 * @return the created URL. 242 */ 243 private String createUrl(final CommandTarget target) 244 { 245 final String targetUrl = target.getUrl(); 246 final StringBuilder buffer = new StringBuilder(128); 247 buffer.append(targetUrl).append('/').append(service); 248 return buffer.toString(); 249 } 250 251 // --- get&set -------------------------------------------------------------- 252 253 /** 254 * Returns the result of the command execution. May be <code>null</code> if 255 * the command has not been executed yet or if the execution of the command 256 * failed without a result. 257 * 258 * @return the result of the command execution. 259 */ 260 @Override 261 public final CommandResult<T> getResult() 262 { 263 return result; 264 } 265 266 // --- business ------------------------------------------------------------- 267 268 /** 269 * Creates the method to be executed with a HTTP client. 270 * 271 * @return the method to be executed with a HTTP client. 272 */ 273 private HttpMethod createMethod() 274 { 275 final PostMethod method = new PostMethod(url); 276 277 for (final CommandArgument<?> argument : arguments) 278 { 279 // FIXME: Ugly API argument.getName().getName() 280 final String name = argument.getName().getName(); 281 final String value = argument.getValue(); 282 if (value != null) 283 { 284 method.addParameter(name, value); 285 } 286 } 287 288 return method; 289 } 290 291 /** 292 * {@inheritDoc} 293 * 294 * @see de.smartics.maven.issue.command.AddVersionCommand#execute() 295 */ 296 @Override 297 public final CommandResult<T> execute(final CommandTarget target) 298 throws CommandException 299 { 300 if (url != null) 301 { 302 throw new CommandException("Command has already been executed: " + url); 303 } 304 305 url = createUrl(target); 306 final HttpMethod method = createMethod(); 307 final int code = target.execute(method); 308 309 final String pageContent = HttpClientUtils.readBodyContent(method); 310 final HttpStatus httpStatus = createStatus(method); 311 312 result = createResult(code, pageContent, httpStatus); 313 return result; 314 } 315 316 /** 317 * Returns the command argument with the specified name. 318 * 319 * @param parameter the parameter that defines the name of the requested 320 * argument. 321 * @return the requested argument. 322 */ 323 protected final CommandArgument<T> getArgument( 324 final CommandParameter<T> parameter) 325 { 326 for (final CommandArgument<T> argument : arguments) 327 { 328 if (parameter.equals(argument.getName())) 329 { 330 return argument; 331 } 332 } 333 334 throw new IllegalArgumentException("Command '" + this.url 335 + "' does not know parameter '" 336 + parameter + "'."); 337 } 338 339 // --- object basics -------------------------------------------------------- 340 341 private HttpStatus createStatus(final HttpMethod method) 342 { 343 final int code = method.getStatusCode(); 344 final String text = method.getStatusText(); 345 final HttpStatus httpStatus = new HttpStatus(code, text); 346 return httpStatus; 347 } 348 349 // CHECKSTYLE:OFF 350 /** 351 * Override if token should be read. 352 * 353 * @return <code>true</code> if token is required to be read from response, 354 * <code>false</code> (the default) otherwise. 355 */ 356 protected boolean isTokenRequiredToRead() // CHECKSTYLE:ON 357 { 358 return false; 359 } 360 361 private CommandResult<T> createResult(final int code, 362 final String pageContent, final HttpStatus httpStatus) 363 { 364 final Grabber grabber = new Grabber(pageContent); 365 final String title = grabber.grabTitle(); 366 final String token = grabber.grabToken(); 367 368 final CommandResult.Page page = 369 new CommandResult.Page(token, title, pageContent); 370 final Expectation expectation = checkExpectation(page); 371 final String description = getCommandResultDescription(page, expectation); 372 return new CommandResult<T>(code, httpStatus, description, page, 373 expectation); 374 } 375 376 /** 377 * Returns the description to the result. This description explains what 378 * happened in a short sentence. This is the essence of the command's result. 379 * The description of a command result may be <code>null</code>, if the 380 * command has no substantial result. A non-substantial result, which 381 * justifies a value of <code>null</code>, would be a navigation command to a 382 * certain page. 383 * 384 * @param page the resulting page to be analyzed to create the correct 385 * description. 386 * @param expecation information about the analyzed result. 387 * @return the description to the result. 388 */ 389 protected abstract String getCommandResultDescription(final Page page, 390 final Expectation expecation); 391 392 /** 393 * Analyzes the returned page and returns the expectation report. 394 * 395 * @param page the returned page to check. 396 * @return the report on the check. 397 */ 398 protected abstract Expectation checkExpectation(final Page page); 399 400 // CHECKSTYLE:OFF 401 /** 402 * {@inheritDoc} 403 * 404 * @see java.lang.Object#toString() 405 */ 406 @Override 407 public String toString() // CHECKSTYLE:ON 408 { 409 final StringBuilder buffer = new StringBuilder(128); 410 411 if (url != null) 412 { 413 buffer.append(url); 414 } 415 416 for (final CommandArgument<?> argument : arguments) 417 { 418 buffer.append("\n ").append(argument); 419 } 420 421 return buffer.toString(); 422 } 423 }