Changeset 4608
- Timestamp:
- 12/12/08 10:24:52 (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
TI01-discovery/branches/ws-Discovery2-upgrade/src/ndg/services/discovery/DiscoveryServiceSkeleton.java
r4186 r4608 14 14 15 15 /** 16 * Implementation class for NERC DataGrid Discovery Web Service. 17 * Based on DiscoveryServiceSkeleton.java which is generated by WSDL2Java 18 * @author Matt Pritchard 19 */ 20 public class DiscoveryServiceSkeleton 21 { 16 * Implementation class for NERC DataGrid Discovery Web Service. Based on DiscoveryServiceSkeleton.java which is generated by WSDL2Java 17 * 18 * @author Matt Pritchard 19 */ 20 public class DiscoveryServiceSkeleton { 22 21 // Obtain a suitable logger. 23 private static Logger logger = Logger.getLogger(DiscoveryServiceSkeleton.class.getName()); 24 25 public static final String[] LIST_NAMES = {"presentFormatList", "orderByFieldList", "scopeList", 26 "termTypeList", "spatialOperatorList"}; 22 private static Logger logger = Logger.getLogger(DiscoveryServiceSkeleton.class.getName()); 23 24 public static final String[] LIST_NAMES = { "presentFormatList", "orderByFieldList", "scopeList", "termTypeList", "spatialOperatorList" }; 27 25 28 26 // Constants representing the possible, valid values for the various input list types 29 27 public static final String FULL_TEXT_TERM_TYPE = "fullText"; 28 30 29 public static final String AUTHOR_TERM_TYPE = "author"; 30 31 31 public static final String PARAMETER_TERM_TYPE = "parameter"; 32 32 33 public static final String OVERLAPS_OPERATOR_TYPE = "overlaps"; 34 33 35 public static final String WITHIN_OPERATOR_TYPE = "within"; 36 34 37 public static final String NO_OVERLAP_OPERATOR_TYPE = "doesNotOverlap"; 35 38 36 39 public static final String DELIMITER = "-------------------------------------"; 37 40 38 41 public static final String ORIGINAL_FORMAT = "original"; 42 39 43 public static final String DC_FORMAT = "DC"; 44 40 45 public static final String DIF_FORMAT = "DIF"; 46 41 47 public static final String MDIP_FORMAT = "MDIP"; 48 42 49 public static final String ISO19139_FORMAT = "ISO19139"; 50 43 51 public static final String ISO19115_FORMAT = "ISO19115"; 44 52 45 53 // to_tsvector ignores the '_' character; as a work around replace this with the following string 46 54 // - primarily for use in the scope field 47 55 public static final String UNDERSCORE_REPLACEMENT = "UNDERSCORE"; 48 56 49 public static final String INVALID_LIST_NAME_MESSAGE = 50 "Invalid list name: please use getListNames to get the valid list names"; 57 public static final String INVALID_LIST_NAME_MESSAGE = "Invalid list name: please use getListNames to get the valid list names"; 51 58 52 59 public static final String DATE_ORDER = "date"; 53 60 54 61 public static final String DATACENTRE_ORDER = "dataCentre"; 55 62 56 63 /** 57 64 * Helper method for checking validity of a format 65 * 58 66 * @param format 59 67 * @return true if format if valid, false otherwise 60 68 */ 61 public static boolean isValidFormat(String format) 62 { 63 String[] allFormats = {ORIGINAL_FORMAT, DC_FORMAT, DIF_FORMAT, 64 MDIP_FORMAT, ISO19139_FORMAT, ISO19115_FORMAT}; 65 for (int i = 0; i < allFormats.length; i++) 66 { 69 public static boolean isValidFormat(String format) { 70 String[] allFormats = { ORIGINAL_FORMAT, DC_FORMAT, DIF_FORMAT, MDIP_FORMAT, ISO19139_FORMAT, ISO19115_FORMAT }; 71 for (int i = 0; i < allFormats.length; i++) { 67 72 if (allFormats[i].equals(format)) 68 73 return true; … … 70 75 return false; 71 76 } 72 /** 73 * Performs the doFullTextSearch operation 74 * @param discoveryserviceapi.DoSearchDocument containing search request 77 78 /** 79 * Performs the doFullTextSearch operation 80 * 81 * @param discoveryserviceapi.DoSearchDocument 82 * containing search request 75 83 * @return discoveryserviceapi.DoSearchReturnDocument containing search result 76 84 */ 77 public discoveryserviceapi.DoSearchReturnDocument doSearch 78 (discoveryserviceapi.DoSearchDocument request ) 79 80 { 85 public discoveryserviceapi.DoSearchReturnDocument doSearch(discoveryserviceapi.DoSearchDocument request) 86 87 { 81 88 logger.info(DELIMITER); 82 89 logger.info("doSearch() invoked"); 83 90 logger.info(DELIMITER); 84 discoveryserviceapi.DoSearchReturnDocument response = 85 discoveryserviceapi.DoSearchReturnDocument.Factory.newInstance(); 91 discoveryserviceapi.DoSearchReturnDocument response = discoveryserviceapi.DoSearchReturnDocument.Factory.newInstance(); 86 92 discoveryserviceapi.SearchReturnType responseContent = response.addNewDoSearchReturn(); 87 93 88 89 94 SearchSummary myResult = null; 90 95 91 96 // Execute the search 92 try 93 { 97 try { 94 98 // firstly, parse the request message and set up the search agent 95 99 logger.info("Preparing search agent for search"); … … 99 103 myResult = agent.doSearch(); 100 104 logger.info("Search completed"); 101 } 102 catch (Exception e) 103 { 104 String errorMessage = "Unable to retrieve result - reason : " + e.getMessage(); 105 } catch (Exception e) { 106 String errorMessage = "Unable to retrieve result - reason : " + e.getMessage(); 105 107 logger.severe(errorMessage); 106 responseContent.setStatus( false);108 responseContent.setStatus(false); 107 109 responseContent.setStatusMessage(errorMessage); 108 110 return response; 109 111 } 110 112 111 if (myResult != null) 112 { 113 if (myResult != null) { 113 114 logger.info("Checking results..."); 114 responseContent.setStatus( myResult.getStatus());115 responseContent.setStatusMessage( myResult.getStatusMessage());116 responseContent.setResultId( myResult.getResultId());117 if ( myResult.getStatus()) // successful search115 responseContent.setStatus(myResult.getStatus()); 116 responseContent.setStatusMessage(myResult.getStatusMessage()); 117 responseContent.setResultId(myResult.getResultId()); 118 if (myResult.getStatus()) // successful search 118 119 { 119 120 logger.info("Search successfully returned results"); 120 responseContent.setHits( myResult.getHits());121 responseContent.setHits(myResult.getHits()); 121 122 122 123 Vector summaryDocuments = myResult.getDocuments(); 123 124 if (summaryDocuments != null ) 125 { 124 125 if (summaryDocuments != null) { 126 126 logger.info("Search returned some matching documents"); 127 127 discoveryserviceapi.DocumentsType responseDocuments = responseContent.addNewDocuments(); 128 for (Iterator i = summaryDocuments.iterator(); i.hasNext(); ) 129 { 130 Hashtable document = (Hashtable)i.next(); 131 responseDocuments.addDocument( (String)document.get("name") ); 128 for (Iterator i = summaryDocuments.iterator(); i.hasNext();) { 129 Hashtable document = (Hashtable) i.next(); 130 responseDocuments.addDocument((String) document.get("name")); 132 131 } 133 132 134 133 logger.info("Adding found documents to response"); 135 responseContent.setDocuments( responseDocuments ); 136 } 137 else 138 { 134 responseContent.setDocuments(responseDocuments); 135 } else { 139 136 logger.info("null data set returned from search - this may indicate a problem"); 140 137 } 141 } 142 else //unsuccessful search 138 } else // unsuccessful search 143 139 { 144 140 logger.warning("Search status was unsuccessful - this may indicate a problem"); 145 responseContent.setHits( 0 ); 146 } 147 } 148 else 149 { 141 responseContent.setHits(0); 142 } 143 } else { 150 144 logger.warning("null data set returned from search - this may indicate a problem"); 151 145 } 152 146 153 147 logger.info("doSearch() completed successfully"); 154 148 … … 156 150 } 157 151 158 159 /** 160 * Prepare the search agent with all the passed in parameters from the WS request, ready to run 161 * a search 162 * 163 * @param request - DoSearchDocument object passed from webservice call 152 /** 153 * Prepare the search agent with all the passed in parameters from the WS request, ready to run a search 154 * 155 * @param request - 156 * DoSearchDocument object passed from webservice call 164 157 * @return SearchAgent object with all required initialisation done for running a search query 165 * @throws DiscoveryWSException if problems encountered whilst doing the set up166 * /167 private SearchAgent setupSearchAgent(DoSearchDocument request) throws DiscoveryWSException168 {158 * @throws DiscoveryWSException 159 * if problems encountered whilst doing the set up 160 */ 161 private SearchAgent setupSearchAgent(DoSearchDocument request) throws DiscoveryWSException { 169 162 logger.info("Setting up search agent"); 170 163 discoveryserviceapi.SearchType requestContent = request.getDoSearch(); 171 164 SearchAgent agent = new SearchAgent(); 172 165 173 166 // check if we're doing a term type search - if so, check there is a term to search on 174 if (Utilities.isStringDefined(requestContent.getTerm())) 175 { 167 if (Utilities.isStringDefined(requestContent.getTerm())) { 176 168 logger.info("Term set to search on: " + requestContent.getTerm()); 177 169 agent.setTerm(requestContent.getTerm()); 178 170 } 179 171 180 172 // check if we're doing a term type search - if so, check there is a term to search on 181 if (Utilities.isStringDefined(requestContent.getTermType())) 182 { 183 logger.info("Term type set: " + requestContent.getTermType()); 173 if (Utilities.isStringDefined(requestContent.getTermType())) { 174 logger.info("Term type set: " + requestContent.getTermType()); 184 175 logger.info("- checking for term value"); 185 176 186 if (Utilities.isStringDefined(requestContent.getTerm())) 187 { 177 if (Utilities.isStringDefined(requestContent.getTerm())) { 188 178 logger.info("Term set - will do term search"); 189 179 agent.setTermType(requestContent.getTermType()); 190 } 191 else 192 { 180 } else { 193 181 logger.info("No term set - ignoring term type search specification"); 194 182 } 195 183 } 196 197 if ( requestContent.isSetStart() ) 198 { 184 185 if (requestContent.isSetStart()) { 199 186 logger.info("Start position of results set set to: " + requestContent.getStart()); 200 agent.setStart( requestContent.getStart() ); 201 } 202 203 if ( requestContent.isSetHowMany() ) 204 { 187 agent.setStart(requestContent.getStart()); 188 } 189 190 if (requestContent.isSetHowMany()) { 205 191 logger.info("Result set size set to: " + requestContent.getHowMany()); 206 agent.setHowMany( requestContent.getHowMany() ); 207 } 208 209 if ( requestContent.isSetOrderBy() ) 210 { 192 agent.setHowMany(requestContent.getHowMany()); 193 } 194 195 if (requestContent.isSetOrderBy()) { 211 196 logger.info("Results ordered by: " + requestContent.getOrderBy()); 212 agent.setOrderByField( requestContent.getOrderBy().toString() ); 213 if ( requestContent.isSetOrderByDirection() ) 214 { 197 agent.setOrderByField(requestContent.getOrderBy().toString()); 198 if (requestContent.isSetOrderByDirection()) { 215 199 logger.info("Results ordering direction: " + requestContent.getOrderByDirection().toString()); 216 agent.setOrderByDirection( requestContent.getOrderByDirection().toString() ); 217 } 218 } 219 220 if ( requestContent.sizeOfScopeArray() > 0) 221 { 222 for (int i=0; i<requestContent.sizeOfScopeArray(); i++) 223 { 200 agent.setOrderByDirection(requestContent.getOrderByDirection().toString()); 201 } 202 } 203 204 if (requestContent.sizeOfScopeArray() > 0) { 205 for (int i = 0; i < requestContent.sizeOfScopeArray(); i++) { 224 206 logger.info("Adding search scope: " + requestContent.getScopeArray(i)); 225 agent.addNewScope( requestContent.getScopeArray(i).toString() ); 226 } 227 } 228 229 if ( requestContent.isSetBoundingBox() ) 230 { 207 agent.addNewScope(requestContent.getScopeArray(i).toString()); 208 } 209 } 210 211 if (requestContent.isSetBoundingBox()) { 231 212 logger.info("Adding bounding box data"); 232 agent.setLimitWest( requestContent.getBoundingBox().getLimitWest() ); 233 agent.setLimitSouth( requestContent.getBoundingBox().getLimitSouth() ); 234 agent.setLimitEast( requestContent.getBoundingBox().getLimitEast() ); 235 agent.setLimitNorth( requestContent.getBoundingBox().getLimitNorth() ); 236 if ( requestContent.isSetSpatialOperator() ) 237 { 213 agent.setLimitWest(requestContent.getBoundingBox().getLimitWest()); 214 agent.setLimitSouth(requestContent.getBoundingBox().getLimitSouth()); 215 agent.setLimitEast(requestContent.getBoundingBox().getLimitEast()); 216 agent.setLimitNorth(requestContent.getBoundingBox().getLimitNorth()); 217 if (requestContent.isSetSpatialOperator()) { 238 218 logger.info("Adding spatial operator: " + requestContent.getSpatialOperator()); 239 agent.setSpatialOperator( requestContent.getSpatialOperator());219 agent.setSpatialOperator(requestContent.getSpatialOperator()); 240 220 } 241 221 } … … 246 226 // is set 247 227 // - NB, would be better to tighten up WSDL definition so that you can/cannot set things up wrongly 248 if (requestContent.isSetDateRange()) 249 { 228 if (requestContent.isSetDateRange()) { 250 229 logger.info("Adding temporal range"); 251 230 // NB, need to check that both end and start dates are set; if these are accessed via the daterange object 252 231 // and are nulls, an exception is thrown 253 try 254 { 255 agent.setDateRangeStart( requestContent.getDateRange().getDateRangeStart() ); 256 agent.setDateRangeEnd( requestContent.getDateRange().getDateRangeEnd() ); 257 } 258 catch (Exception e) 259 { 260 String errorMessage = "Invalid time range specified - detail: " + e.getMessage(); 232 try { 233 agent.setDateRangeStart(requestContent.getDateRange().getDateRangeStart()); 234 agent.setDateRangeEnd(requestContent.getDateRange().getDateRangeEnd()); 235 } catch (Exception e) { 236 String errorMessage = "Invalid time range specified - detail: " + e.getMessage(); 261 237 logger.warning(errorMessage); 262 238 throw new DiscoveryWSException(errorMessage); 263 239 } 264 240 } 265 241 266 242 logger.info("Search agent ready for searching"); 267 243 return agent; 268 244 } 269 245 270 271 246 /** 272 247 * Performs the doPresent operation 273 * @param discoveryserviceapi.DoPresentDocument containing search request 248 * 249 * @param discoveryserviceapi.DoPresentDocument 250 * containing search request 274 251 * @return discoveryserviceapi.DoPresentReturnDocument containing search result 275 * @throws DiscoveryWSException 276 */ 277 public discoveryserviceapi.DoPresentReturnDocument doPresent 278 (discoveryserviceapi.DoPresentDocument request ) throws DiscoveryWSException 279 { 252 * @throws DiscoveryWSException 253 */ 254 public discoveryserviceapi.DoPresentReturnDocument doPresent(discoveryserviceapi.DoPresentDocument request) throws DiscoveryWSException { 280 255 logger.info(DELIMITER); 281 256 logger.info("doPresent() invoked"); 282 257 logger.info(DELIMITER); 283 284 discoveryserviceapi.DoPresentReturnDocument response = 285 discoveryserviceapi.DoPresentReturnDocument.Factory.newInstance(); 258 259 discoveryserviceapi.DoPresentReturnDocument response = discoveryserviceapi.DoPresentReturnDocument.Factory.newInstance(); 286 260 discoveryserviceapi.PresentReturnType responseContent = response.addNewDoPresentReturn(); 287 261 … … 289 263 discoveryserviceapi.PresentType requestContent = request.getDoPresent(); 290 264 PresentAgent agent = new PresentAgent(); 291 265 292 266 // Get the format 293 if ( requestContent.isSetFormat() ) 294 { 295 agent.setFormat( requestContent.getFormat().toString() ); 296 } 267 if (requestContent.isSetFormat()) { 268 agent.setFormat(requestContent.getFormat().toString()); 269 } 270 271 String requestedFormat = requestContent.getFormat().toString(); 297 272 298 273 // Get the names of documents to fetch & pass to PresentAgent 299 274 discoveryserviceapi.DocumentsType documents = requestContent.getDocuments(); 300 275 String[] documentsString = documents.getDocumentArray(); 276 301 277 logger.info("Building list of " + documentsString.length + " documents for present"); 278 302 279 Vector<String> documentsVector = new Vector<String>(); 303 for (int i=0; i<documentsString.length; i++ ) 304 { 280 for (int i = 0; i < documentsString.length; i++) { 305 281 documentsVector.add(documentsString[i]); 306 282 } 307 agent.setDocumentNames( documentsVector ); 308 309 try 310 {283 284 agent.setDocumentNames(documentsVector); 285 286 try { 311 287 logger.info("Running doPresent() to retrieve documents"); 288 312 289 PresentSummary summary = agent.doPresent(); 313 responseContent.setStatus( summary.getStatus() ); 314 responseContent.setStatusMessage( summary.getStatusMessage() ); 290 responseContent.setStatus(summary.getStatus()); 291 responseContent.setStatusMessage(summary.getStatusMessage()); 292 315 293 // NB, if no results are returned, the status is false 316 if (summary.getStatus()) 317 { 294 if (summary.getStatus()) { 318 295 logger.info("doPresent completed successfully - preparing results for return"); 296 319 297 Vector resultDocuments = summary.getDocuments(); 320 Iterator it = resultDocuments.iterator(); 298 299 // Problem: list of xml docs returned does NOT match that in the documentsVector, therefore must make sure it matches 300 Vector<String> rearrangedDocsVector = new Vector<String>(); 301 302 for (int i = 0; i < documentsString.length; i++) { 303 String thisDocNameID = documentsString[i]; 304 305 // search for it in the xml doc returned -loop through all docs 306 Iterator thisBit = resultDocuments.iterator(); 307 Boolean idOccurs; 308 309 while (thisBit.hasNext()) { 310 String xmlDocAsString = (String) thisBit.next(); 311 312 idOccurs = xmlDocAsString.contains(Utilities.resultSetCheckString(requestedFormat, thisDocNameID)); 313 314 //if search term found in xml doc, add it to the vector. If not, remove that doc from the list in the first place 315 if (idOccurs) { 316 // logger.info("Match found for: " + dataentSearchTerm); 317 rearrangedDocsVector.add(xmlDocAsString); 318 } 319 320 } 321 } 322 Integer resDocsSize = resultDocuments.size(); 323 Integer rearrDocsSize = rearrangedDocsVector.size(); 324 325 logger.info("Number in rearrangedDocsVector: " + rearrDocsSize.toString()); 326 logger.info("Number in resultDocuments: " + resDocsSize.toString()); 327 328 // swap the comment-outs below to revert to pre-update for easy testing! 329 // Iterator it = resultDocuments.iterator(); 330 Iterator it = rearrangedDocsVector.iterator(); 331 321 332 discoveryserviceapi.DocumentsType returnDocuments = responseContent.addNewDocuments(); 322 while ( it.hasNext() ) 323 {324 returnDocuments.addDocument( (String)it.next());333 334 while (it.hasNext()) { 335 returnDocuments.addDocument((String) it.next()); 325 336 } 326 337 } 327 } 328 catch (Exception e) 329 { 330 responseContent.setStatusMessage("Error creating doPresentReturnDocument : " + e.toString() ); 331 } 338 } catch (Exception e) { 339 responseContent.setStatusMessage("Error creating doPresentReturnDocument : " + e.toString()); 340 } 341 332 342 logger.info("doPresent() completed - returning response"); 333 343 … … 335 345 } 336 346 337 /** 338 * Performs the getListNames operation 339 * - returns list of valid strings to use as input to the getList service 340 * @param discoveryserviceapi.GetListNamesDocument containing request 347 /** 348 * Performs the getListNames operation - returns list of valid strings to use as input to the getList service 349 * 350 * @param discoveryserviceapi.GetListNamesDocument 351 * containing request 341 352 * @return discoveryserviceapi.GetListNamesReturnDocument containing result 342 353 */ 343 public discoveryserviceapi.GetListNamesReturnDocument getListNames 344 (discoveryserviceapi.GetListNamesDocument request ) 345 { 346 logger.info("getListNames() invoked"); 354 public discoveryserviceapi.GetListNamesReturnDocument getListNames(discoveryserviceapi.GetListNamesDocument request) { 355 logger.info("getListNames() invoked"); 347 356 discoveryserviceapi.GetListNamesReturnDocument response = discoveryserviceapi.GetListNamesReturnDocument.Factory.newInstance(); 348 357 discoveryserviceapi.GetListNamesReturnType responseContent = response.addNewGetListNamesReturn(); 349 discoveryserviceapi.GetListNamesReturnType.ListNames listNames = 358 discoveryserviceapi.GetListNamesReturnType.ListNames listNames = responseContent.addNewListNames(); 350 359 351 360 for (int i = 0; i < LIST_NAMES.length; i++) … … 356 365 } 357 366 358 367 /** 359 368 * Performs the getList operation 360 * @param discoveryserviceapi.GetListDocument containing request 369 * 370 * @param discoveryserviceapi.GetListDocument 371 * containing request 361 372 * @return discoveryserviceapi.GetListReturnDocument containing result 362 * @throws DiscoveryWSException 363 */ 364 public discoveryserviceapi.GetListReturnDocument getList 365 (discoveryserviceapi.GetListDocument request ) throws DiscoveryWSException 366 { 367 logger.info("getList() invoked"); 373 * @throws DiscoveryWSException 374 */ 375 public discoveryserviceapi.GetListReturnDocument getList(discoveryserviceapi.GetListDocument request) throws DiscoveryWSException { 376 logger.info("getList() invoked"); 368 377 discoveryserviceapi.GetListReturnDocument response = discoveryserviceapi.GetListReturnDocument.Factory.newInstance(); 369 378 discoveryserviceapi.GetListReturnType responseContent = response.addNewGetListReturn(); 370 discoveryserviceapi.GetListReturnType.List list = 379 discoveryserviceapi.GetListReturnType.List list = responseContent.addNewList(); 371 380 372 381 // parse the request message 373 382 discoveryserviceapi.GetListType requestContent = request.getGetList(); 374 383 String listName = requestContent.getListName(); 375 list.setName( listName ); 376 377 if ( listName.equals("presentFormatList") ) 378 { 384 list.setName(listName); 385 386 if (listName.equals("presentFormatList")) { 379 387 list.addListMember(ORIGINAL_FORMAT); 380 388 list.addListMember(DC_FORMAT); … … 382 390 list.addListMember(MDIP_FORMAT); 383 391 list.addListMember(ISO19139_FORMAT); 384 //list.addListMember("moles"); 385 } 386 else if ( requestContent.getListName().equals("orderByFieldList") ) 387 { 392 // list.addListMember("moles"); 393 } else if (requestContent.getListName().equals("orderByFieldList")) { 388 394 list.addListMember(DATE_ORDER); 389 395 list.addListMember(DATACENTRE_ORDER); 390 } 391 else if ( requestContent.getListName().equals("scopeList") ) 392 { 396 } else if (requestContent.getListName().equals("scopeList")) { 393 397 list.addListMember("NERC"); 394 398 list.addListMember("NERC_DDC"); 395 399 list.addListMember("MDIP"); 396 400 list.addListMember("DPPP"); 397 } 398 else if ( requestContent.getListName().equals("termTypeList") ) 399 { 401 } else if (requestContent.getListName().equals("termTypeList")) { 400 402 list.addListMember(FULL_TEXT_TERM_TYPE); 401 403 list.addListMember(AUTHOR_TERM_TYPE); 402 404 list.addListMember(PARAMETER_TERM_TYPE); 403 } 404 else if ( requestContent.getListName().equals("spatialOperatorList") ) 405 { 405 } else if (requestContent.getListName().equals("spatialOperatorList")) { 406 406 list.addListMember(OVERLAPS_OPERATOR_TYPE); 407 407 list.addListMember(NO_OVERLAP_OPERATOR_TYPE); 408 408 list.addListMember(WITHIN_OPERATOR_TYPE); 409 } 410 else 411 { 409 } else { 412 410 logger.warning(INVALID_LIST_NAME_MESSAGE); 413 411 throw new DiscoveryWSException(INVALID_LIST_NAME_MESSAGE); 414 412 } 415 413 416 414 logger.info("getList() completed successfully"); 417 415 return response; 418 416 } 419 417 420 418 }
Note: See TracChangeset
for help on using the changeset viewer.