You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UploadAction.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. package org.apache.maven.archiva.web.action;
  2. /*
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. */
  20. import java.io.File;
  21. import java.io.FileInputStream;
  22. import java.io.FileOutputStream;
  23. import java.io.IOException;
  24. import java.text.DateFormat;
  25. import java.text.SimpleDateFormat;
  26. import java.util.ArrayList;
  27. import java.util.Calendar;
  28. import java.util.Date;
  29. import java.util.Collections;
  30. import java.util.List;
  31. import java.util.TimeZone;
  32. import org.apache.archiva.checksum.ChecksumAlgorithm;
  33. import org.apache.archiva.checksum.ChecksummedFile;
  34. import org.apache.maven.archiva.common.utils.VersionComparator;
  35. import org.apache.maven.archiva.common.utils.VersionUtil;
  36. import org.apache.maven.archiva.configuration.ArchivaConfiguration;
  37. import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
  38. import org.apache.maven.archiva.model.ArchivaProjectModel;
  39. import org.apache.maven.archiva.model.ArchivaRepositoryMetadata;
  40. import org.apache.maven.archiva.model.ArtifactReference;
  41. import org.apache.maven.archiva.model.SnapshotVersion;
  42. import org.apache.maven.archiva.repository.ManagedRepositoryContent;
  43. import org.apache.maven.archiva.repository.RepositoryContentFactory;
  44. import org.apache.maven.archiva.repository.RepositoryException;
  45. import org.apache.maven.archiva.repository.RepositoryNotFoundException;
  46. import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers;
  47. import org.apache.maven.archiva.repository.audit.AuditEvent;
  48. import org.apache.maven.archiva.repository.audit.AuditListener;
  49. import org.apache.maven.archiva.repository.audit.Auditable;
  50. import org.apache.maven.archiva.repository.metadata.MetadataTools;
  51. import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
  52. import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader;
  53. import org.apache.maven.archiva.repository.metadata.RepositoryMetadataWriter;
  54. import org.apache.maven.archiva.repository.project.ProjectModelException;
  55. import org.apache.maven.archiva.repository.project.ProjectModelWriter;
  56. import org.apache.maven.archiva.repository.project.writers.ProjectModel400Writer;
  57. import org.apache.maven.archiva.security.ArchivaSecurityException;
  58. import org.apache.maven.archiva.security.PrincipalNotFoundException;
  59. import org.apache.maven.archiva.security.UserRepositories;
  60. import org.apache.maven.archiva.security.ArchivaXworkUser;
  61. import org.apache.struts2.ServletActionContext;
  62. import com.opensymphony.xwork2.ActionContext;
  63. import com.opensymphony.xwork2.Preparable;
  64. import com.opensymphony.xwork2.Validateable;
  65. import org.apache.commons.io.FilenameUtils;
  66. import org.apache.commons.lang.StringUtils;
  67. /**
  68. * Upload an artifact using Jakarta file upload in webwork. If set by the user a pom will also be generated. Metadata
  69. * will also be updated if one exists, otherwise it would be created.
  70. *
  71. * @author <a href="mailto:wsmoak@apache.org">Wendy Smoak</a>
  72. * @author <a href="mailto:oching@apache.org">Maria Odea Ching</a>
  73. * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="uploadAction"
  74. */
  75. public class UploadAction
  76. extends PlexusActionSupport
  77. implements Validateable, Preparable, Auditable
  78. {
  79. /**
  80. * @plexus.requirement
  81. */
  82. private RepositoryContentConsumers consumers;
  83. /**
  84. * @plexus.requirement
  85. */
  86. private ArchivaXworkUser archivaXworkUser;
  87. /**
  88. * The groupId of the artifact to be deployed.
  89. */
  90. private String groupId;
  91. /**
  92. * The artifactId of the artifact to be deployed.
  93. */
  94. private String artifactId;
  95. /**
  96. * The version of the artifact to be deployed.
  97. */
  98. private String version;
  99. /**
  100. * The packaging of the artifact to be deployed.
  101. */
  102. private String packaging;
  103. /**
  104. * The classifier of the artifact to be deployed.
  105. */
  106. private String classifier;
  107. /**
  108. * The temporary file representing the artifact to be deployed.
  109. */
  110. private File artifactFile;
  111. /**
  112. * The content type of the artifact to be deployed.
  113. */
  114. private String artifactContentType;
  115. /**
  116. * The original filename of the uploaded artifact file.
  117. */
  118. private String artifactFilename;
  119. /**
  120. * The temporary file representing the pom to be deployed alongside the artifact.
  121. */
  122. private File pomFile;
  123. /**
  124. * The content type of the pom file.
  125. */
  126. private String pomContentType;
  127. /**
  128. * The original filename of the uploaded pom file.
  129. */
  130. private String pomFilename;
  131. /**
  132. * The repository where the artifact is to be deployed.
  133. */
  134. private String repositoryId;
  135. /**
  136. * Flag whether to generate a pom for the artifact or not.
  137. */
  138. private boolean generatePom;
  139. /**
  140. * List of managed repositories to deploy to.
  141. */
  142. private List<String> managedRepoIdList;
  143. /**
  144. * @plexus.requirement
  145. */
  146. private UserRepositories userRepositories;
  147. /**
  148. * @plexus.requirement role-hint="default"
  149. */
  150. private ArchivaConfiguration configuration;
  151. /**
  152. * @plexus.requirement
  153. */
  154. private RepositoryContentFactory repositoryFactory;
  155. /**
  156. * @plexus.requirement role="org.apache.maven.archiva.repository.audit.AuditListener"
  157. */
  158. private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
  159. private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[] { ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
  160. private ProjectModelWriter pomWriter = new ProjectModel400Writer();
  161. public void setArtifact( File file )
  162. {
  163. this.artifactFile = file;
  164. }
  165. public void setArtifactContentType( String contentType )
  166. {
  167. this.artifactContentType = contentType;
  168. }
  169. public void setArtifactFileName( String filename )
  170. {
  171. this.artifactFilename = filename;
  172. }
  173. public void setPom( File file )
  174. {
  175. this.pomFile = file;
  176. }
  177. public void setPomContentType( String contentType )
  178. {
  179. this.pomContentType = contentType;
  180. }
  181. public void setPomFileName( String filename )
  182. {
  183. this.pomFilename = filename;
  184. }
  185. public String getGroupId()
  186. {
  187. return groupId;
  188. }
  189. public void setGroupId( String groupId )
  190. {
  191. this.groupId = groupId;
  192. }
  193. public String getArtifactId()
  194. {
  195. return artifactId;
  196. }
  197. public void setArtifactId( String artifactId )
  198. {
  199. this.artifactId = artifactId;
  200. }
  201. public String getVersion()
  202. {
  203. return version;
  204. }
  205. public void setVersion( String version )
  206. {
  207. this.version = version;
  208. }
  209. public String getPackaging()
  210. {
  211. return packaging;
  212. }
  213. public void setPackaging( String packaging )
  214. {
  215. this.packaging = packaging;
  216. }
  217. public String getClassifier()
  218. {
  219. return classifier;
  220. }
  221. public void setClassifier( String classifier )
  222. {
  223. this.classifier = classifier;
  224. }
  225. public String getRepositoryId()
  226. {
  227. return repositoryId;
  228. }
  229. public void setRepositoryId( String repositoryId )
  230. {
  231. this.repositoryId = repositoryId;
  232. }
  233. public boolean isGeneratePom()
  234. {
  235. return generatePom;
  236. }
  237. public void setGeneratePom( boolean generatePom )
  238. {
  239. this.generatePom = generatePom;
  240. }
  241. public List<String> getManagedRepoIdList()
  242. {
  243. return managedRepoIdList;
  244. }
  245. public void setManagedRepoIdList( List<String> managedRepoIdList )
  246. {
  247. this.managedRepoIdList = managedRepoIdList;
  248. }
  249. public void prepare()
  250. {
  251. managedRepoIdList =
  252. new ArrayList<String>( configuration.getConfiguration().getManagedRepositoriesAsMap().keySet() );
  253. }
  254. public String input()
  255. {
  256. return INPUT;
  257. }
  258. private void reset()
  259. {
  260. // reset the fields so the form is clear when
  261. // the action returns to the jsp page
  262. groupId = "";
  263. artifactId = "";
  264. version = "";
  265. packaging = "";
  266. classifier = "";
  267. artifactFile = null;
  268. artifactContentType = "";
  269. artifactFilename = "";
  270. pomFile = null;
  271. pomContentType = "";
  272. pomFilename = "";
  273. repositoryId = "";
  274. generatePom = false;
  275. }
  276. public String doUpload()
  277. {
  278. try
  279. {
  280. ManagedRepositoryConfiguration repoConfig =
  281. configuration.getConfiguration().findManagedRepositoryById( repositoryId );
  282. ArtifactReference artifactReference = new ArtifactReference();
  283. artifactReference.setArtifactId( artifactId );
  284. artifactReference.setGroupId( groupId );
  285. artifactReference.setVersion( version );
  286. artifactReference.setClassifier( classifier );
  287. artifactReference.setType( packaging );
  288. ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
  289. String artifactPath = repository.toPath( artifactReference );
  290. int lastIndex = artifactPath.lastIndexOf( '/' );
  291. File targetPath = new File( repoConfig.getLocation(), artifactPath.substring( 0, lastIndex ) );
  292. Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
  293. int newBuildNumber = -1;
  294. String timestamp = null;
  295. File metadataFile = getMetadata( targetPath.getAbsolutePath() );
  296. ArchivaRepositoryMetadata metadata = getMetadata( metadataFile );
  297. if (VersionUtil.isSnapshot(version))
  298. {
  299. TimeZone timezone = TimeZone.getTimeZone( "UTC" );
  300. DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
  301. fmt.setTimeZone( timezone );
  302. timestamp = fmt.format( lastUpdatedTimestamp );
  303. if ( metadata.getSnapshotVersion() != null )
  304. {
  305. newBuildNumber = metadata.getSnapshotVersion().getBuildNumber() + 1;
  306. }
  307. else
  308. {
  309. metadata.setSnapshotVersion( new SnapshotVersion() );
  310. newBuildNumber = 1;
  311. }
  312. }
  313. if ( !targetPath.exists() )
  314. {
  315. targetPath.mkdirs();
  316. }
  317. String filename = artifactPath.substring( lastIndex + 1 );
  318. if ( VersionUtil.isSnapshot( version ) )
  319. {
  320. filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
  321. }
  322. try
  323. {
  324. copyFile( artifactFile, targetPath, filename );
  325. consumers.executeConsumers( repoConfig, repository.toFile( artifactReference ) );
  326. }
  327. catch ( IOException ie )
  328. {
  329. addActionError( "Error encountered while uploading file: " + ie.getMessage() );
  330. return ERROR;
  331. }
  332. String pomFilename = filename;
  333. if( classifier != null && !"".equals( classifier ) )
  334. {
  335. pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
  336. }
  337. pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
  338. if ( generatePom )
  339. {
  340. try
  341. {
  342. File generatedPomFile = createPom( targetPath, pomFilename );
  343. consumers.executeConsumers( repoConfig, generatedPomFile );
  344. }
  345. catch ( IOException ie )
  346. {
  347. addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
  348. return ERROR;
  349. }
  350. catch ( ProjectModelException pe )
  351. {
  352. addActionError( "Error encountered while generating pom file: " + pe.getMessage() );
  353. return ERROR;
  354. }
  355. }
  356. if ( pomFile != null && pomFile.length() > 0 )
  357. {
  358. try
  359. {
  360. copyFile( pomFile, targetPath, pomFilename );
  361. consumers.executeConsumers( repoConfig, new File( targetPath, pomFilename ) );
  362. }
  363. catch ( IOException ie )
  364. {
  365. addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
  366. return ERROR;
  367. }
  368. }
  369. updateMetadata( metadata, metadataFile, lastUpdatedTimestamp, timestamp, newBuildNumber );
  370. String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version +
  371. "\' was successfully deployed to repository \'" + repositoryId + "\'";
  372. triggerAuditEvent( getPrincipal(), repositoryId, groupId + ":" + artifactId + ":" + version, AuditEvent.UPLOAD_FILE );
  373. addActionMessage( msg );
  374. reset();
  375. return SUCCESS;
  376. }
  377. catch ( RepositoryNotFoundException re )
  378. {
  379. addActionError( "Target repository cannot be found: " + re.getMessage() );
  380. return ERROR;
  381. }
  382. catch ( RepositoryException rep )
  383. {
  384. addActionError( "Repository exception: " + rep.getMessage() );
  385. return ERROR;
  386. }
  387. }
  388. private String getPrincipal()
  389. {
  390. return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
  391. }
  392. private void copyFile( File sourceFile, File targetPath, String targetFilename )
  393. throws IOException
  394. {
  395. FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
  396. try
  397. {
  398. FileInputStream input = new FileInputStream( sourceFile );
  399. int i = 0;
  400. while ( ( i = input.read() ) != -1 )
  401. {
  402. out.write( i );
  403. }
  404. out.flush();
  405. }
  406. finally
  407. {
  408. out.close();
  409. }
  410. }
  411. private File createPom( File targetPath, String filename )
  412. throws IOException, ProjectModelException
  413. {
  414. ArchivaProjectModel projectModel = new ArchivaProjectModel();
  415. projectModel.setGroupId( groupId );
  416. projectModel.setArtifactId( artifactId );
  417. projectModel.setVersion( version );
  418. projectModel.setPackaging( packaging );
  419. File pomFile = new File( targetPath, filename);
  420. pomWriter.write( projectModel, pomFile );
  421. return pomFile;
  422. }
  423. private File getMetadata( String targetPath )
  424. {
  425. String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
  426. return new File( artifactPath, MetadataTools.MAVEN_METADATA );
  427. }
  428. private ArchivaRepositoryMetadata getMetadata( File metadataFile )
  429. throws RepositoryMetadataException
  430. {
  431. ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
  432. if ( metadataFile.exists() )
  433. {
  434. metadata = RepositoryMetadataReader.read( metadataFile );
  435. }
  436. return metadata;
  437. }
  438. /**
  439. * Update artifact level metadata. If it does not exist, create the metadata.
  440. *
  441. * @param metadata
  442. */
  443. private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp,
  444. String timestamp, int buildNumber )
  445. throws RepositoryMetadataException
  446. {
  447. List<String> availableVersions = new ArrayList<String>();
  448. String latestVersion = version;
  449. if ( metadataFile.exists() )
  450. {
  451. availableVersions = metadata.getAvailableVersions();
  452. Collections.sort( availableVersions, VersionComparator.getInstance() );
  453. if ( !availableVersions.contains( version ) )
  454. {
  455. availableVersions.add( version );
  456. }
  457. latestVersion = availableVersions.get( availableVersions.size() - 1 );
  458. }
  459. else
  460. {
  461. availableVersions.add( version );
  462. metadata.setGroupId( groupId );
  463. metadata.setArtifactId( artifactId );
  464. }
  465. if ( metadata.getGroupId() == null )
  466. {
  467. metadata.setGroupId( groupId );
  468. }
  469. if ( metadata.getArtifactId() == null )
  470. {
  471. metadata.setArtifactId( artifactId );
  472. }
  473. metadata.setLatestVersion( latestVersion );
  474. metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
  475. metadata.setAvailableVersions( availableVersions );
  476. if ( !VersionUtil.isSnapshot( version ) )
  477. {
  478. metadata.setReleasedVersion( latestVersion );
  479. }
  480. else
  481. {
  482. metadata.getSnapshotVersion().setBuildNumber( buildNumber );
  483. metadata.getSnapshotVersion().setTimestamp( timestamp );
  484. }
  485. RepositoryMetadataWriter.write( metadata, metadataFile );
  486. ChecksummedFile checksum = new ChecksummedFile( metadataFile );
  487. checksum.fixChecksums( algorithms );
  488. }
  489. public void validate()
  490. {
  491. try
  492. {
  493. // is this enough check for the repository permission?
  494. if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
  495. {
  496. addActionError( "User is not authorized to upload in repository " + repositoryId );
  497. }
  498. if ( artifactFile == null || artifactFile.length() == 0 )
  499. {
  500. addActionError( "Please add a file to upload." );
  501. }
  502. if ( !VersionUtil.isVersion( version ) )
  503. {
  504. addActionError( "Invalid version." );
  505. }
  506. }
  507. catch ( PrincipalNotFoundException pe )
  508. {
  509. addActionError( pe.getMessage() );
  510. }
  511. catch ( ArchivaSecurityException ae )
  512. {
  513. addActionError( ae.getMessage() );
  514. }
  515. }
  516. public void addAuditListener( AuditListener listener )
  517. {
  518. this.auditListeners.add( listener );
  519. }
  520. public void clearAuditListeners()
  521. {
  522. this.auditListeners.clear();
  523. }
  524. public void removeAuditListener( AuditListener listener )
  525. {
  526. this.auditListeners.remove( listener );
  527. }
  528. private void triggerAuditEvent( String user, String repositoryId, String resource, String action )
  529. {
  530. AuditEvent event = new AuditEvent( repositoryId, user, resource, action );
  531. event.setRemoteIP( ServletActionContext.getRequest().getRemoteAddr() );
  532. for ( AuditListener listener : auditListeners )
  533. {
  534. listener.auditEvent( event );
  535. }
  536. }
  537. }