]> source.dussan.org Git - archiva.git/blob
f3a0fee9e439d1bd343ac46989e6287a736eed22
[archiva.git] /
1 package org.apache.archiva.metadata.repository.cassandra;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import com.google.common.base.Predicate;
23 import com.google.common.collect.Iterables;
24 import me.prettyprint.cassandra.serializers.LongSerializer;
25 import me.prettyprint.cassandra.serializers.StringSerializer;
26 import me.prettyprint.cassandra.service.template.ColumnFamilyResult;
27 import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate;
28 import me.prettyprint.cassandra.service.template.ColumnFamilyUpdater;
29 import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate;
30 import me.prettyprint.hector.api.Keyspace;
31 import me.prettyprint.hector.api.beans.ColumnSlice;
32 import me.prettyprint.hector.api.beans.OrderedRows;
33 import me.prettyprint.hector.api.beans.Row;
34 import me.prettyprint.hector.api.exceptions.HInvalidRequestException;
35 import me.prettyprint.hector.api.factory.HFactory;
36 import me.prettyprint.hector.api.mutation.MutationResult;
37 import me.prettyprint.hector.api.mutation.Mutator;
38 import me.prettyprint.hector.api.query.QueryResult;
39 import me.prettyprint.hector.api.query.RangeSlicesQuery;
40 import org.apache.archiva.checksum.ChecksumAlgorithm;
41 import org.apache.archiva.configuration.ArchivaConfiguration;
42 import org.apache.archiva.metadata.QueryParameter;
43 import org.apache.archiva.metadata.model.ArtifactMetadata;
44 import org.apache.archiva.metadata.model.CiManagement;
45 import org.apache.archiva.metadata.model.Dependency;
46 import org.apache.archiva.metadata.model.FacetedMetadata;
47 import org.apache.archiva.metadata.model.IssueManagement;
48 import org.apache.archiva.metadata.model.License;
49 import org.apache.archiva.metadata.model.MailingList;
50 import org.apache.archiva.metadata.model.MetadataFacet;
51 import org.apache.archiva.metadata.model.MetadataFacetFactory;
52 import org.apache.archiva.metadata.model.Organization;
53 import org.apache.archiva.metadata.model.ProjectMetadata;
54 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
55 import org.apache.archiva.metadata.model.ProjectVersionReference;
56 import org.apache.archiva.metadata.model.Scm;
57 import org.apache.archiva.metadata.repository.AbstractMetadataRepository;
58 import org.apache.archiva.metadata.repository.MetadataRepository;
59 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
60 import org.apache.archiva.metadata.repository.MetadataResolutionException;
61 import org.apache.archiva.metadata.repository.MetadataService;
62 import org.apache.archiva.metadata.repository.RepositorySession;
63 import org.apache.archiva.metadata.repository.cassandra.model.ArtifactMetadataModel;
64 import org.apache.archiva.metadata.repository.cassandra.model.MetadataFacetModel;
65 import org.apache.archiva.metadata.repository.cassandra.model.Namespace;
66 import org.apache.archiva.metadata.repository.cassandra.model.Project;
67 import org.apache.archiva.metadata.repository.cassandra.model.ProjectVersionMetadataModel;
68 import org.apache.archiva.metadata.repository.cassandra.model.Repository;
69 import org.apache.commons.lang.StringUtils;
70 import org.modelmapper.ModelMapper;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73
74 import javax.annotation.ParametersAreNonnullByDefault;
75 import java.time.Instant;
76 import java.time.ZonedDateTime;
77 import java.util.*;
78 import java.util.function.BiFunction;
79 import java.util.function.Consumer;
80 import java.util.stream.Collectors;
81 import java.util.stream.Stream;
82 import java.util.stream.StreamSupport;
83
84 import static org.apache.archiva.metadata.model.ModelInfo.STORAGE_TZ;
85 import static org.apache.archiva.metadata.repository.cassandra.CassandraUtils.*;
86 import static org.apache.archiva.metadata.repository.cassandra.model.ColumnNames.*;
87
88 /**
89  * @author Olivier Lamy
90  * @since 2.0.0
91  */
92 @ParametersAreNonnullByDefault
93 public class CassandraMetadataRepository
94     extends AbstractMetadataRepository implements MetadataRepository
95 {
96
97     private static final String ARTIFACT_METADATA_MODEL_KEY = "artifactMetadataModel.key";
98     private Logger logger = LoggerFactory.getLogger( getClass() );
99
100     private ArchivaConfiguration configuration;
101
102     private final CassandraArchivaManager cassandraArchivaManager;
103
104     private final ColumnFamilyTemplate<String, String> projectVersionMetadataTemplate;
105
106     private final ColumnFamilyTemplate<String, String> projectTemplate;
107
108     private final ColumnFamilyTemplate<String, String> artifactMetadataTemplate;
109
110     private final ColumnFamilyTemplate<String, String> metadataFacetTemplate;
111
112     private final ColumnFamilyTemplate<String, String> mailingListTemplate;
113
114     private final ColumnFamilyTemplate<String, String> licenseTemplate;
115
116     private final ColumnFamilyTemplate<String, String> dependencyTemplate;
117
118     private final ColumnFamilyTemplate<String, String> checksumTemplate;
119
120     private final Keyspace keyspace;
121
122     private final StringSerializer ss = StringSerializer.get();
123
124     public CassandraMetadataRepository( MetadataService metadataService,
125                                         ArchivaConfiguration configuration,
126                                         CassandraArchivaManager cassandraArchivaManager )
127     {
128         super( metadataService );
129         this.configuration = configuration;
130         this.cassandraArchivaManager = cassandraArchivaManager;
131         this.keyspace = cassandraArchivaManager.getKeyspace();
132
133         this.projectVersionMetadataTemplate =
134             new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
135                                               cassandraArchivaManager.getProjectVersionMetadataFamilyName(), //
136                                               StringSerializer.get(), //
137                                               StringSerializer.get() );
138
139         this.projectTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
140                                                                  cassandraArchivaManager.getProjectFamilyName(), //
141                                                                  //
142                                                                  StringSerializer.get(), //
143                                                                  StringSerializer.get() );
144
145         this.artifactMetadataTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
146                                                                           cassandraArchivaManager.getArtifactMetadataFamilyName(),
147                                                                           StringSerializer.get(), //
148                                                                           StringSerializer.get() );
149
150         this.metadataFacetTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
151                                                                        cassandraArchivaManager.getMetadataFacetFamilyName(),
152                                                                        //
153                                                                        StringSerializer.get(), //
154                                                                        StringSerializer.get() );
155
156         this.mailingListTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
157                                                                      cassandraArchivaManager.getMailingListFamilyName(),
158                                                                      //
159                                                                      StringSerializer.get(), //
160                                                                      StringSerializer.get() );
161
162         this.licenseTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
163                                                                  cassandraArchivaManager.getLicenseFamilyName(),
164                                                                  //
165                                                                  StringSerializer.get(), //
166                                                                  StringSerializer.get() );
167
168         this.dependencyTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
169                                                                     cassandraArchivaManager.getDependencyFamilyName(),
170                                                                     //
171                                                                     StringSerializer.get(), //
172                                                                     StringSerializer.get() );
173
174         this.checksumTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
175                 cassandraArchivaManager.getChecksumFamilyName(),
176                 //
177                 StringSerializer.get(), //
178                 StringSerializer.get() );
179     }
180
181
182     /**
183      * if the repository doesn't exist it will be created
184      *
185      * @param repositoryId
186      * @return
187      */
188     public Repository getOrCreateRepository( String repositoryId )
189         throws MetadataRepositoryException
190     {
191         String cf = cassandraArchivaManager.getRepositoryFamilyName();
192
193         QueryResult<OrderedRows<String, String, String>> result = HFactory //
194             .createRangeSlicesQuery( keyspace, StringSerializer.get(), StringSerializer.get(),
195                                      StringSerializer.get() ) //
196             .setColumnFamily( cf ) //
197             .setColumnNames( REPOSITORY_NAME.toString() ) //
198             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
199             .execute();
200
201         if ( result.get().getCount() < 1 )
202         {
203             // we need to create the repository
204             Repository repository = new Repository( repositoryId );
205
206             try
207             {
208                 MutationResult mutationResult = HFactory.createMutator( keyspace, StringSerializer.get() ) //
209                     .addInsertion( repositoryId, cf,
210                                    CassandraUtils.column( REPOSITORY_NAME.toString(), repository.getName() ) ) //
211                     .execute();
212                 logger.debug( "time to insert repository: {}", mutationResult.getExecutionTimeMicro() );
213                 return repository;
214             }
215             catch ( HInvalidRequestException e )
216             {
217                 logger.error( e.getMessage(), e );
218                 throw new MetadataRepositoryException( e.getMessage(), e );
219             }
220
221         }
222
223         return new Repository(
224             result.get().getList().get( 0 ).getColumnSlice().getColumnByName( REPOSITORY_NAME.toString() ).getValue() );
225     }
226
227
228     protected Repository getRepository( String repositoryId )
229         throws MetadataRepositoryException
230     {
231
232         QueryResult<OrderedRows<String, String, String>> result = HFactory //
233             .createRangeSlicesQuery( keyspace, StringSerializer.get(), StringSerializer.get(),
234                                      StringSerializer.get() ) //
235             .setColumnFamily( cassandraArchivaManager.getRepositoryFamilyName() ) //
236             .setColumnNames( REPOSITORY_NAME.toString() ) //
237             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
238             .execute();
239         return ( result.get().getCount() > 0 ) ? new Repository( repositoryId ) : null;
240     }
241
242     @Override
243     public void updateNamespace( RepositorySession session, String repositoryId, String namespaceId )
244         throws MetadataRepositoryException
245     {
246         updateOrAddNamespace( repositoryId, namespaceId );
247     }
248
249     private Namespace updateOrAddNamespace( String repositoryId, String namespaceId )
250         throws MetadataRepositoryException
251     {
252         try
253         {
254             Repository repository = getOrCreateRepository( repositoryId );
255
256             String key =
257                 new Namespace.KeyBuilder().withNamespace( namespaceId ).withRepositoryId( repositoryId ).build();
258
259             Namespace namespace = getNamespace( repositoryId, namespaceId );
260             if ( namespace == null )
261             {
262                 String cf = cassandraArchivaManager.getNamespaceFamilyName();
263                 namespace = new Namespace( namespaceId, repository );
264                 HFactory.createMutator( keyspace, StringSerializer.get() )
265                     //  values
266                     .addInsertion( key, cf, CassandraUtils.column( NAME.toString(), namespace.getName() ) ) //
267                     .addInsertion( key, cf, CassandraUtils.column( REPOSITORY_NAME.toString(), repository.getName() ) ) //
268                     .execute();
269             }
270
271             return namespace;
272         }
273         catch ( HInvalidRequestException e )
274         {
275             logger.error( e.getMessage(), e );
276             throw new MetadataRepositoryException( e.getMessage(), e );
277         }
278     }
279
280     protected Namespace getNamespace( String repositoryId, String namespaceId )
281     {
282
283         QueryResult<OrderedRows<String, String, String>> result = HFactory //
284             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
285             .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
286             .setColumnNames( REPOSITORY_NAME.toString(), NAME.toString() ) //
287             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
288             .addEqualsExpression( NAME.toString(), namespaceId ) //
289             .execute();
290         if ( result.get().getCount() > 0 )
291         {
292             ColumnSlice<String, String> columnSlice = result.get().getList().get( 0 ).getColumnSlice();
293             return new Namespace( getStringValue( columnSlice, NAME.toString() ), //
294                                   new Repository( getStringValue( columnSlice, REPOSITORY_NAME.toString() ) ) );
295
296         }
297         return null;
298     }
299
300
301     @Override
302     public void removeNamespace( RepositorySession session, String repositoryId, String namespaceId )
303         throws MetadataRepositoryException
304     {
305
306         try
307         {
308             String key = new Namespace.KeyBuilder() //
309                 .withNamespace( namespaceId ) //
310                 .withRepositoryId( repositoryId ) //
311                 .build();
312
313             HFactory.createMutator( cassandraArchivaManager.getKeyspace(), new StringSerializer() ) //
314                 .addDeletion( key, cassandraArchivaManager.getNamespaceFamilyName() ) //
315                 .execute();
316
317             QueryResult<OrderedRows<String, String, String>> result = HFactory //
318                 .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
319                 .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
320                 .setColumnNames( REPOSITORY_NAME.toString() ) //
321                 .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
322                 .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
323                 .execute();
324
325             for ( Row<String, String, String> row : result.get() )
326             {
327                 this.projectTemplate.deleteRow( row.getKey() );
328             }
329
330             result = HFactory //
331                 .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
332                 .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
333                 .setColumnNames( REPOSITORY_NAME.toString() ) //
334                 .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
335                 .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
336                 .execute();
337
338             for ( Row<String, String, String> row : result.get() )
339             {
340                 this.projectVersionMetadataTemplate.deleteRow( row.getKey() );
341                 removeMailingList( row.getKey() );
342             }
343
344             result = HFactory //
345                 .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
346                 .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
347                 .setColumnNames( REPOSITORY_NAME.toString() ) //
348                 .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
349                 .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
350                 .execute();
351
352             for ( Row<String, String, String> row : result.get() )
353             {
354                 this.artifactMetadataTemplate.deleteRow( row.getKey() );
355             }
356
357             result = HFactory //
358                 .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
359                 .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
360                 .setColumnNames( REPOSITORY_NAME.toString() ) //
361                 .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
362                 .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
363                 .execute();
364
365             for ( Row<String, String, String> row : result.get() )
366             {
367                 this.metadataFacetTemplate.deleteRow( row.getKey() );
368             }
369
370         }
371         catch ( HInvalidRequestException e )
372         {
373             logger.error( e.getMessage(), e );
374             throw new MetadataRepositoryException( e.getMessage(), e );
375         }
376     }
377
378
379     @Override
380     public void removeRepository( RepositorySession session, final String repositoryId )
381         throws MetadataRepositoryException
382     {
383
384         // TODO use cql queries to delete all
385         List<String> namespacesKey = new ArrayList<>();
386
387         QueryResult<OrderedRows<String, String, String>> result = HFactory //
388             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
389             .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
390             .setColumnNames( REPOSITORY_NAME.toString() ) //
391             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
392             .execute();
393
394         for ( Row<String, String, String> row : result.get().getList() )
395         {
396             namespacesKey.add( row.getKey() );
397         }
398
399         HFactory.createMutator( cassandraArchivaManager.getKeyspace(), ss ) //
400             .addDeletion( namespacesKey, cassandraArchivaManager.getNamespaceFamilyName() ) //
401             .execute();
402
403         //delete repositoryId
404         HFactory.createMutator( cassandraArchivaManager.getKeyspace(), ss ) //
405             .addDeletion( repositoryId, cassandraArchivaManager.getRepositoryFamilyName() ) //
406             .execute();
407
408         result = HFactory //
409             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
410             .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
411             .setColumnNames( REPOSITORY_NAME.toString() ) //
412             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
413             .execute();
414
415         for ( Row<String, String, String> row : result.get() )
416         {
417             this.projectTemplate.deleteRow( row.getKey() );
418         }
419
420         result = HFactory //
421             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
422             .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
423             .setColumnNames( REPOSITORY_NAME.toString() ) //
424             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
425             .execute();
426
427         for ( Row<String, String, String> row : result.get() )
428         {
429             this.projectVersionMetadataTemplate.deleteRow( row.getKey() );
430             removeMailingList( row.getKey() );
431         }
432
433         result = HFactory //
434             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
435             .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
436             .setColumnNames( REPOSITORY_NAME.toString() ) //
437             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
438             .execute();
439
440         for ( Row<String, String, String> row : result.get() )
441         {
442             this.artifactMetadataTemplate.deleteRow( row.getKey() );
443         }
444
445         result = HFactory //
446             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
447             .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
448             .setColumnNames( REPOSITORY_NAME.toString() ) //
449             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
450             .execute();
451
452         for ( Row<String, String, String> row : result.get() )
453         {
454             this.metadataFacetTemplate.deleteRow( row.getKey() );
455         }
456
457
458     }
459
460     // FIXME this one need peformance improvement maybe a cache?
461     @Override
462     public Collection<String> getRootNamespaces( RepositorySession session, final String repoId )
463         throws MetadataResolutionException
464     {
465
466         QueryResult<OrderedRows<String, String, String>> result = HFactory //
467             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
468             .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
469             .setColumnNames( NAME.toString() ) //
470             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
471             .execute();
472
473         Set<String> namespaces = new HashSet<String>( result.get().getCount() );
474
475         for ( Row<String, String, String> row : result.get() )
476         {
477             namespaces.add( StringUtils.substringBefore( getStringValue( row.getColumnSlice(), NAME.toString() ), "." ) );
478         }
479
480         return namespaces;
481     }
482
483     // FIXME this one need peformance improvement maybe a cache?
484     @Override
485     public Collection<String> getChildNamespaces( RepositorySession session, final String repoId, final String namespaceId )
486         throws MetadataResolutionException
487     {
488
489         QueryResult<OrderedRows<String, String, String>> result = HFactory //
490             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
491             .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
492             .setColumnNames( NAME.toString() ) //
493             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
494             .execute();
495
496         List<String> namespaces = new ArrayList<>( result.get().getCount() );
497
498         for ( Row<String, String, String> row : result.get() )
499         {
500             String currentNamespace = getStringValue( row.getColumnSlice(), NAME.toString() );
501             if ( StringUtils.startsWith( currentNamespace, namespaceId ) //
502                 && ( StringUtils.length( currentNamespace ) > StringUtils.length( namespaceId ) ) )
503             {
504                 // store after namespaceId '.' but before next '.'
505                 // call org namespace org.apache.maven.shared -> stored apache
506
507                 String calledNamespace = StringUtils.endsWith( namespaceId, "." ) ? namespaceId : namespaceId + ".";
508                 String storedNamespace = StringUtils.substringAfter( currentNamespace, calledNamespace );
509
510                 storedNamespace = StringUtils.substringBefore( storedNamespace, "." );
511
512                 namespaces.add( storedNamespace );
513             }
514         }
515
516         return namespaces;
517
518     }
519
520     // only use for testing purpose
521     protected List<String> getNamespaces( final String repoId )
522         throws MetadataResolutionException
523     {
524
525         QueryResult<OrderedRows<String, String, String>> result = HFactory //
526             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
527             .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
528             .setColumnNames( NAME.toString() ) //
529             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
530             .execute();
531
532         List<String> namespaces = new ArrayList<>( result.get().getCount() );
533
534         for ( Row<String, String, String> row : result.get() )
535         {
536             namespaces.add( getStringValue( row.getColumnSlice(), NAME.toString() ) );
537         }
538
539         return namespaces;
540     }
541
542
543     @Override
544     public void updateProject( RepositorySession session, String repositoryId, ProjectMetadata projectMetadata )
545         throws MetadataRepositoryException
546     {
547
548         QueryResult<OrderedRows<String, String, String>> result = HFactory //
549             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
550             .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
551             .setColumnNames( PROJECT_ID.toString() ) //
552             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
553             .addEqualsExpression( NAMESPACE_ID.toString(), projectMetadata.getNamespace() ) //
554             .addEqualsExpression( PROJECT_ID.toString(), projectMetadata.getId() ) //
555             .execute();
556
557         // project exists ? if yes return nothing to update here
558         if ( result.get().getCount() > 0 )
559         {
560             return;
561         }
562         else
563         {
564             Namespace namespace = updateOrAddNamespace( repositoryId, projectMetadata.getNamespace() );
565
566             String key =
567                 new Project.KeyBuilder().withProjectId( projectMetadata.getId() ).withNamespace( namespace ).build();
568
569             String cf = cassandraArchivaManager.getProjectFamilyName();
570             projectTemplate.createMutator()
571                 //  values
572                 .addInsertion( key, cf, CassandraUtils.column( PROJECT_ID.toString(), projectMetadata.getId() ) ) //
573                 .addInsertion( key, cf, CassandraUtils.column( REPOSITORY_NAME.toString(), repositoryId ) ) //
574                 .addInsertion( key, cf, CassandraUtils.column( NAMESPACE_ID.toString(), projectMetadata.getNamespace() ) )//
575                 .execute();
576         }
577     }
578
579     @Override
580     public Collection<String> getProjects( RepositorySession session, final String repoId, final String namespace )
581         throws MetadataResolutionException
582     {
583
584         QueryResult<OrderedRows<String, String, String>> result = HFactory //
585             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
586             .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
587             .setColumnNames( PROJECT_ID.toString() ) //
588             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
589             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
590             .execute();
591
592         final Set<String> projects = new HashSet<String>( result.get().getCount() );
593
594         for ( Row<String, String, String> row : result.get() )
595         {
596             projects.add( getStringValue( row.getColumnSlice(), PROJECT_ID.toString() ) );
597         }
598
599         return projects;
600     }
601
602     @Override
603     public void removeProject( RepositorySession session, final String repositoryId, final String namespaceId, final String projectId )
604         throws MetadataRepositoryException
605     {
606
607         String key = new Project.KeyBuilder() //
608             .withProjectId( projectId ) //
609             .withNamespace( new Namespace( namespaceId, new Repository( repositoryId ) ) ) //
610             .build();
611
612         this.projectTemplate.deleteRow( key );
613
614         QueryResult<OrderedRows<String, String, String>> result = HFactory //
615             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
616             .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
617             .setColumnNames( ID.toString() ) //
618             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
619             .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
620             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
621             .execute();
622
623         for ( Row<String, String, String> row : result.get() )
624         {
625             this.projectVersionMetadataTemplate.deleteRow( row.getKey() );
626             removeMailingList( row.getKey() );
627         }
628
629         result = HFactory //
630             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
631             .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
632             .setColumnNames( PROJECT_ID.toString() ) //
633             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
634             .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
635             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
636             .execute();
637
638         for ( Row<String, String, String> row : result.get() )
639         {
640             this.artifactMetadataTemplate.deleteRow( row.getKey() );
641         }
642     }
643
644     @Override
645     public Collection<String> getProjectVersions( RepositorySession session, final String repoId, final String namespace, final String projectId )
646         throws MetadataResolutionException
647     {
648
649         QueryResult<OrderedRows<String, String, String>> result = HFactory //
650             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
651             .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
652             .setColumnNames( PROJECT_VERSION.toString() ) //
653             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
654             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
655             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
656             .execute();
657
658         int count = result.get().getCount();
659
660         if ( count < 1 )
661         {
662             return Collections.emptyList();
663         }
664
665         Set<String> versions = new HashSet<String>( count );
666
667         for ( Row<String, String, String> orderedRows : result.get() )
668         {
669             versions.add( getStringValue( orderedRows.getColumnSlice(), PROJECT_VERSION.toString() ) );
670         }
671
672         return versions;
673
674     }
675
676     @Override
677     public ProjectMetadata getProject( RepositorySession session, final String repoId, final String namespace, final String id )
678         throws MetadataResolutionException
679     {
680
681         QueryResult<OrderedRows<String, String, String>> result = HFactory //
682             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
683             .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
684             .setColumnNames( PROJECT_ID.toString() ) //
685             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
686             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
687             .addEqualsExpression( PROJECT_ID.toString(), id ) //
688             .execute();
689
690         int count = result.get().getCount();
691
692         if ( count < 1 )
693         {
694             return null;
695         }
696
697         ProjectMetadata projectMetadata = new ProjectMetadata();
698         projectMetadata.setId( id );
699         projectMetadata.setNamespace( namespace );
700
701         logger.debug( "getProject repoId: {}, namespace: {}, projectId: {} -> {}", repoId, namespace, id,
702                       projectMetadata );
703
704         return projectMetadata;
705     }
706
707     protected ProjectVersionMetadataModel mapProjectVersionMetadataModel( ColumnSlice<String, String> columnSlice )
708     {
709         ProjectVersionMetadataModel projectVersionMetadataModel = new ProjectVersionMetadataModel();
710         projectVersionMetadataModel.setId( getStringValue( columnSlice, ID.toString() ) );
711         projectVersionMetadataModel.setDescription( getStringValue( columnSlice, DESCRIPTION.toString() ) );
712         projectVersionMetadataModel.setName( getStringValue( columnSlice, NAME.toString() ) );
713         Namespace namespace = new Namespace( getStringValue( columnSlice, NAMESPACE_ID.toString() ), //
714                                              new Repository( getStringValue( columnSlice, REPOSITORY_NAME.toString() ) ) );
715         projectVersionMetadataModel.setNamespace( namespace );
716         projectVersionMetadataModel.setIncomplete(
717             Boolean.parseBoolean( getStringValue( columnSlice, "incomplete" ) ) );
718         projectVersionMetadataModel.setProjectId( getStringValue( columnSlice, PROJECT_ID.toString() ) );
719         projectVersionMetadataModel.setUrl( getStringValue( columnSlice, URL.toString() ) );
720         return projectVersionMetadataModel;
721     }
722
723
724     @Override
725     public void updateProjectVersion( RepositorySession session, String repositoryId, String namespaceId, String projectId,
726                                       ProjectVersionMetadata versionMetadata )
727         throws MetadataRepositoryException
728     {
729         try
730         {
731             Namespace namespace = getNamespace( repositoryId, namespaceId );
732
733             if ( namespace == null )
734             {
735                 updateOrAddNamespace( repositoryId, namespaceId );
736             }
737
738             if ( getProject( session, repositoryId, namespaceId, projectId ) == null )
739             {
740                 ProjectMetadata projectMetadata = new ProjectMetadata();
741                 projectMetadata.setNamespace( namespaceId );
742                 projectMetadata.setId( projectId );
743                 updateProject( session, repositoryId, projectMetadata );
744             }
745
746         }
747         catch ( MetadataResolutionException e )
748         {
749             throw new MetadataRepositoryException( e.getMessage(), e );
750         }
751
752         QueryResult<OrderedRows<String, String, String>> result = HFactory //
753             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
754             .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
755             .setColumnNames( PROJECT_VERSION.toString() ) //
756             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
757             .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
758             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
759             .addEqualsExpression( PROJECT_VERSION.toString(), versionMetadata.getId() ) //
760             .execute();
761
762         ProjectVersionMetadataModel projectVersionMetadataModel = null;
763         boolean creation = true;
764         if ( result.get().getCount() > 0 )
765         {
766             projectVersionMetadataModel =
767                 mapProjectVersionMetadataModel( result.get().getList().get( 0 ).getColumnSlice() );
768             creation = false;
769         }
770         else
771         {
772             projectVersionMetadataModel = getModelMapper().map( versionMetadata, ProjectVersionMetadataModel.class );
773         }
774
775         projectVersionMetadataModel.setProjectId( projectId );
776         projectVersionMetadataModel.setNamespace( new Namespace( namespaceId, new Repository( repositoryId ) ) );
777
778         projectVersionMetadataModel.setCiManagement( versionMetadata.getCiManagement() );
779         projectVersionMetadataModel.setIssueManagement( versionMetadata.getIssueManagement() );
780         projectVersionMetadataModel.setOrganization( versionMetadata.getOrganization() );
781         projectVersionMetadataModel.setScm( versionMetadata.getScm() );
782
783         projectVersionMetadataModel.setMailingLists( versionMetadata.getMailingLists() );
784         projectVersionMetadataModel.setDependencies( versionMetadata.getDependencies() );
785         projectVersionMetadataModel.setLicenses( versionMetadata.getLicenses() );
786
787         // we don't test of repository and namespace really exist !
788         String key = new ProjectVersionMetadataModel.KeyBuilder() //
789             .withRepository( repositoryId ) //
790             .withNamespace( namespaceId ) //
791             .withProjectId( projectId ) //
792             .withProjectVersion( versionMetadata.getVersion() ) //
793             .withId( versionMetadata.getId() ) //
794             .build();
795
796         // FIXME nested objects to store!!!
797         if ( creation )
798         {
799             String cf = cassandraArchivaManager.getProjectVersionMetadataFamilyName();
800             Mutator<String> mutator = projectVersionMetadataTemplate.createMutator()
801                 //  values
802                 .addInsertion( key, cf, column( PROJECT_ID.toString(), projectId ) ) //
803                 .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
804                 .addInsertion( key, cf, column( NAMESPACE_ID.toString(), namespaceId ) )//
805                 .addInsertion( key, cf, column( PROJECT_VERSION.toString(), versionMetadata.getVersion() ) ); //
806
807             addInsertion( mutator, key, cf, DESCRIPTION.toString(), versionMetadata.getDescription() );
808
809             addInsertion( mutator, key, cf, NAME.toString(), versionMetadata.getName() );
810
811             addInsertion( mutator, key, cf, "incomplete", Boolean.toString( versionMetadata.isIncomplete() ) );
812
813             addInsertion( mutator, key, cf, URL.toString(), versionMetadata.getUrl() );
814             {
815                 CiManagement ci = versionMetadata.getCiManagement();
816                 if ( ci != null )
817                 {
818                     addInsertion( mutator, key, cf, "ciManagement.system", ci.getSystem() );
819                     addInsertion( mutator, key, cf, "ciManagement.url", ci.getUrl() );
820                 }
821             }
822
823             {
824                 IssueManagement issueManagement = versionMetadata.getIssueManagement();
825
826                 if ( issueManagement != null )
827                 {
828                     addInsertion( mutator, key, cf, "issueManagement.system", issueManagement.getSystem() );
829                     addInsertion( mutator, key, cf, "issueManagement.url", issueManagement.getUrl() );
830                 }
831             }
832
833             {
834                 Organization organization = versionMetadata.getOrganization();
835                 if ( organization != null )
836                 {
837                     addInsertion( mutator, key, cf, "organization.name", organization.getName() );
838                     addInsertion( mutator, key, cf, "organization.url", organization.getUrl() );
839                 }
840             }
841
842             {
843                 Scm scm = versionMetadata.getScm();
844                 if ( scm != null )
845                 {
846                     addInsertion( mutator, key, cf, "scm.url", scm.getUrl() );
847                     addInsertion( mutator, key, cf, "scm.connection", scm.getConnection() );
848                     addInsertion( mutator, key, cf, "scm.developerConnection", scm.getDeveloperConnection() );
849                 }
850             }
851
852             recordMailingList( key, versionMetadata.getMailingLists() );
853
854             recordLicenses( key, versionMetadata.getLicenses() );
855
856             recordDependencies( key, versionMetadata.getDependencies(), repositoryId );
857
858             MutationResult mutationResult = mutator.execute();
859         }
860         else
861         {
862             ColumnFamilyUpdater<String, String> updater = projectVersionMetadataTemplate.createUpdater( key );
863             addUpdateStringValue( updater, PROJECT_ID.toString(), projectId );
864             addUpdateStringValue( updater, REPOSITORY_NAME.toString(), repositoryId );
865             addUpdateStringValue( updater, NAMESPACE_ID.toString(), namespaceId );
866             addUpdateStringValue( updater, PROJECT_VERSION.toString(), versionMetadata.getVersion() );
867             addUpdateStringValue( updater, DESCRIPTION.toString(), versionMetadata.getDescription() );
868
869             addUpdateStringValue( updater, NAME.toString(), versionMetadata.getName() );
870
871             updater.setString( "incomplete", Boolean.toString( versionMetadata.isIncomplete() ) );
872             addUpdateStringValue( updater, URL.toString(), versionMetadata.getUrl() );
873
874             {
875                 CiManagement ci = versionMetadata.getCiManagement();
876                 if ( ci != null )
877                 {
878                     addUpdateStringValue( updater, "ciManagement.system", ci.getSystem() );
879                     addUpdateStringValue( updater, "ciManagement.url", ci.getUrl() );
880                 }
881             }
882             {
883                 IssueManagement issueManagement = versionMetadata.getIssueManagement();
884                 if ( issueManagement != null )
885                 {
886                     addUpdateStringValue( updater, "issueManagement.system", issueManagement.getSystem() );
887                     addUpdateStringValue( updater, "issueManagement.url", issueManagement.getUrl() );
888                 }
889             }
890             {
891                 Organization organization = versionMetadata.getOrganization();
892                 if ( organization != null )
893                 {
894                     addUpdateStringValue( updater, "organization.name", organization.getName() );
895                     addUpdateStringValue( updater, "organization.url", organization.getUrl() );
896                 }
897             }
898             {
899                 Scm scm = versionMetadata.getScm();
900                 if ( scm != null )
901                 {
902                     addUpdateStringValue( updater, "scm.url", scm.getUrl() );
903                     addUpdateStringValue( updater, "scm.connection", scm.getConnection() );
904                     addUpdateStringValue( updater, "scm.developerConnection", scm.getDeveloperConnection() );
905                 }
906             }
907
908             // update is a delete record
909             removeMailingList( key );
910             recordMailingList( key, versionMetadata.getMailingLists() );
911
912             removeLicenses( key );
913             recordLicenses( key, versionMetadata.getLicenses() );
914
915             removeDependencies( key );
916             recordDependencies( key, versionMetadata.getDependencies(), repositoryId );
917
918             projectVersionMetadataTemplate.update( updater );
919
920         }
921
922         ArtifactMetadataModel artifactMetadataModel = new ArtifactMetadataModel();
923         artifactMetadataModel.setRepositoryId( repositoryId );
924         artifactMetadataModel.setNamespace( namespaceId );
925         artifactMetadataModel.setProject( projectId );
926         artifactMetadataModel.setProjectVersion( versionMetadata.getVersion() );
927         artifactMetadataModel.setVersion( versionMetadata.getVersion() );
928         updateFacets( versionMetadata, artifactMetadataModel );
929
930     }
931
932
933     @Override
934     public ProjectVersionMetadata getProjectVersion( RepositorySession session, final String repoId, final String namespace,
935                                                      final String projectId, final String projectVersion )
936         throws MetadataResolutionException
937     {
938
939         QueryResult<OrderedRows<String, String, String>> result = HFactory //
940             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
941             .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
942             .setColumnNames( PROJECT_VERSION.toString() ) //
943             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
944             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
945             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
946             .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
947             .execute();
948
949         if ( result.get().getCount() < 1 )
950         {
951             return null;
952         }
953
954         String key = result.get().iterator().next().getKey();
955
956         ColumnFamilyResult<String, String> columnFamilyResult = this.projectVersionMetadataTemplate.queryColumns( key );
957
958         if ( !columnFamilyResult.hasResults() )
959         {
960             return null;
961         }
962
963         ProjectVersionMetadata projectVersionMetadata = new ProjectVersionMetadata();
964         projectVersionMetadata.setId( columnFamilyResult.getString( PROJECT_VERSION.toString() ) );
965         projectVersionMetadata.setDescription( columnFamilyResult.getString( DESCRIPTION.toString() ) );
966         projectVersionMetadata.setName( columnFamilyResult.getString( NAME.toString() ) );
967
968         projectVersionMetadata.setIncomplete( Boolean.parseBoolean( columnFamilyResult.getString( "incomplete" ) ) );
969
970         projectVersionMetadata.setUrl( columnFamilyResult.getString( URL.toString() ) );
971         {
972             String ciUrl = columnFamilyResult.getString( "ciManagement.url" );
973             String ciSystem = columnFamilyResult.getString( "ciManagement.system" );
974
975             if ( StringUtils.isNotEmpty( ciSystem ) || StringUtils.isNotEmpty( ciUrl ) )
976             {
977                 projectVersionMetadata.setCiManagement( new CiManagement( ciSystem, ciUrl ) );
978             }
979         }
980         {
981             String issueUrl = columnFamilyResult.getString( "issueManagement.url" );
982             String issueSystem = columnFamilyResult.getString( "issueManagement.system" );
983             if ( StringUtils.isNotEmpty( issueSystem ) || StringUtils.isNotEmpty( issueUrl ) )
984             {
985                 projectVersionMetadata.setIssueManagement( new IssueManagement( issueSystem, issueUrl ) );
986             }
987         }
988         {
989             String organizationUrl = columnFamilyResult.getString( "organization.url" );
990             String organizationName = columnFamilyResult.getString( "organization.name" );
991             if ( StringUtils.isNotEmpty( organizationUrl ) || StringUtils.isNotEmpty( organizationName ) )
992             {
993                 projectVersionMetadata.setOrganization( new Organization( organizationName, organizationUrl ) );
994             }
995         }
996         {
997             String devConn = columnFamilyResult.getString( "scm.developerConnection" );
998             String conn = columnFamilyResult.getString( "scm.connection" );
999             String url = columnFamilyResult.getString( "scm.url" );
1000             if ( StringUtils.isNotEmpty( devConn ) || StringUtils.isNotEmpty( conn ) || StringUtils.isNotEmpty( url ) )
1001             {
1002                 projectVersionMetadata.setScm( new Scm( conn, devConn, url ) );
1003             }
1004         }
1005         projectVersionMetadata.setMailingLists( getMailingLists( key ) );
1006         projectVersionMetadata.setLicenses( getLicenses( key ) );
1007         projectVersionMetadata.setDependencies( getDependencies( key ) );
1008         // facets
1009
1010         result = HFactory //
1011             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1012             .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1013             .setColumnNames( FACET_ID.toString(), KEY.toString(), VALUE.toString(), NAME.toString() ) //
1014             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
1015             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
1016             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
1017             .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
1018             .execute();
1019
1020         Map<String, Map<String, String>> metadataFacetsPerFacetIds = new HashMap<>();
1021
1022         for ( Row<String, String, String> row : result.get() )
1023         {
1024             ColumnSlice<String, String> columnSlice = row.getColumnSlice();
1025             String facetId = getStringValue( columnSlice, FACET_ID.toString() );
1026             Map<String, String> metaValues = metadataFacetsPerFacetIds.get( facetId );
1027             if ( metaValues == null )
1028             {
1029                 metaValues = new HashMap<>();
1030                 metadataFacetsPerFacetIds.put( facetId, metaValues );
1031             }
1032             metaValues.put( getStringValue( columnSlice, KEY.toString() ), getStringValue( columnSlice, VALUE.toString() ) );
1033         }
1034
1035         if ( !metadataFacetsPerFacetIds.isEmpty() )
1036         {
1037             for ( Map.Entry<String, Map<String, String>> entry : metadataFacetsPerFacetIds.entrySet() )
1038             {
1039                 MetadataFacetFactory metadataFacetFactory = getFacetFactory( entry.getKey() );
1040                 if ( metadataFacetFactory != null )
1041                 {
1042                     MetadataFacet metadataFacet = metadataFacetFactory.createMetadataFacet();
1043                     metadataFacet.fromProperties( entry.getValue() );
1044                     projectVersionMetadata.addFacet( metadataFacet );
1045                 }
1046             }
1047         }
1048
1049         return projectVersionMetadata;
1050     }
1051
1052     protected void recordChecksums( String repositoryId, String artifactMetadataKey, Map<String, String> checksums)
1053     {
1054         if ( checksums == null || checksums.isEmpty() )
1055         {
1056             return;
1057         }
1058         Mutator<String> checksumMutator = this.checksumTemplate.createMutator();
1059         for ( Map.Entry<String, String> entry : checksums.entrySet())
1060         {
1061             // we don't care about the key as the real used one with the projectVersionMetadata
1062             String keyChecksums = UUID.randomUUID().toString();
1063             String cfChecksums = cassandraArchivaManager.getChecksumFamilyName();
1064
1065             addInsertion( checksumMutator, keyChecksums, cfChecksums, ARTIFACT_METADATA_MODEL_KEY,
1066                     artifactMetadataKey );
1067             addInsertion( checksumMutator, keyChecksums, cfChecksums, CHECKSUM_ALG.toString(), entry.getKey());
1068             addInsertion( checksumMutator, keyChecksums, cfChecksums, CHECKSUM_VALUE.toString(),
1069                     entry.getValue() );
1070             addInsertion(checksumMutator, keyChecksums, cfChecksums, REPOSITORY_NAME.toString(), repositoryId);
1071
1072         }
1073         checksumMutator.execute();
1074     }
1075
1076     protected void removeChecksums( String artifactMetadataKey )
1077     {
1078
1079         QueryResult<OrderedRows<String, String, String>> result =
1080                 HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1081                         .setColumnFamily( cassandraArchivaManager.getChecksumFamilyName() ) //
1082                         .setColumnNames( CHECKSUM_ALG.toString() ) //
1083                         .setRowCount( Integer.MAX_VALUE ) //
1084                         .addEqualsExpression(ARTIFACT_METADATA_MODEL_KEY, artifactMetadataKey ) //
1085                         .execute();
1086
1087         if ( result.get().getCount() < 1 )
1088         {
1089             return;
1090         }
1091
1092         for ( Row<String, String, String> row : result.get() )
1093         {
1094             this.checksumTemplate.deleteRow( row.getKey() );
1095         }
1096
1097     }
1098
1099     protected Map<String, String> getChecksums( String artifactMetadataKey )
1100     {
1101         Map<String, String> checksums = new HashMap<>();
1102
1103         QueryResult<OrderedRows<String, String, String>> result =
1104                 HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1105                         .setColumnFamily( cassandraArchivaManager.getChecksumFamilyName() ) //
1106                         .setColumnNames( ARTIFACT_METADATA_MODEL_KEY, REPOSITORY_NAME.toString(),
1107                                 CHECKSUM_ALG.toString(), CHECKSUM_VALUE.toString() ) //
1108                         .setRowCount( Integer.MAX_VALUE ) //
1109                         .addEqualsExpression(ARTIFACT_METADATA_MODEL_KEY, artifactMetadataKey) //
1110                         .execute();
1111         for ( Row<String, String, String> row : result.get() )
1112         {
1113             ColumnFamilyResult<String, String> columnFamilyResult =
1114                     this.checksumTemplate.queryColumns( row.getKey() );
1115
1116             checksums.put(columnFamilyResult.getString(CHECKSUM_ALG.toString()),
1117                     columnFamilyResult.getString(CHECKSUM_VALUE.toString()));
1118         }
1119
1120         return checksums;
1121     }
1122
1123     protected void recordMailingList( String projectVersionMetadataKey, List<MailingList> mailingLists )
1124     {
1125         if ( mailingLists == null || mailingLists.isEmpty() )
1126         {
1127             return;
1128         }
1129         Mutator<String> mailingMutator = this.mailingListTemplate.createMutator();
1130         for ( MailingList mailingList : mailingLists )
1131         {
1132             // we don't care about the key as the real used one with the projectVersionMetadata
1133             String keyMailingList = UUID.randomUUID().toString();
1134             String cfMailingList = cassandraArchivaManager.getMailingListFamilyName();
1135
1136             addInsertion( mailingMutator, keyMailingList, cfMailingList, "projectVersionMetadataModel.key",
1137                           projectVersionMetadataKey );
1138             addInsertion( mailingMutator, keyMailingList, cfMailingList, NAME.toString(), mailingList.getName() );
1139             addInsertion( mailingMutator, keyMailingList, cfMailingList, "mainArchiveUrl",
1140                           mailingList.getMainArchiveUrl() );
1141             addInsertion( mailingMutator, keyMailingList, cfMailingList, "postAddress", mailingList.getPostAddress() );
1142             addInsertion( mailingMutator, keyMailingList, cfMailingList, "subscribeAddress",
1143                           mailingList.getSubscribeAddress() );
1144             addInsertion( mailingMutator, keyMailingList, cfMailingList, "unsubscribeAddress",
1145                           mailingList.getUnsubscribeAddress() );
1146             int idx = 0;
1147             for ( String otherArchive : mailingList.getOtherArchives() )
1148             {
1149                 addInsertion( mailingMutator, keyMailingList, cfMailingList, "otherArchive." + idx, otherArchive );
1150                 idx++;
1151             }
1152
1153         }
1154         mailingMutator.execute();
1155     }
1156
1157     protected void removeMailingList( String projectVersionMetadataKey )
1158     {
1159
1160         QueryResult<OrderedRows<String, String, String>> result =
1161             HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1162                 .setColumnFamily( cassandraArchivaManager.getMailingListFamilyName() ) //
1163                 .setColumnNames( NAME.toString() ) //
1164                 .setRowCount( Integer.MAX_VALUE ) //
1165                 .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1166                 .execute();
1167
1168         if ( result.get().getCount() < 1 )
1169         {
1170             return;
1171         }
1172
1173         for ( Row<String, String, String> row : result.get() )
1174         {
1175             this.mailingListTemplate.deleteRow( row.getKey() );
1176         }
1177
1178     }
1179
1180     protected List<MailingList> getMailingLists( String projectVersionMetadataKey )
1181     {
1182         List<MailingList> mailingLists = new ArrayList<>();
1183
1184         QueryResult<OrderedRows<String, String, String>> result =
1185             HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1186                 .setColumnFamily( cassandraArchivaManager.getMailingListFamilyName() ) //
1187                 .setColumnNames( NAME.toString() ) //
1188                 .setRowCount( Integer.MAX_VALUE ) //
1189                 .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1190                 .execute();
1191         for ( Row<String, String, String> row : result.get() )
1192         {
1193             ColumnFamilyResult<String, String> columnFamilyResult =
1194                 this.mailingListTemplate.queryColumns( row.getKey() );
1195
1196             MailingList mailingList = new MailingList();
1197             mailingList.setName( columnFamilyResult.getString( NAME.toString() ) );
1198             mailingList.setMainArchiveUrl( columnFamilyResult.getString( "mainArchiveUrl" ) );
1199             mailingList.setPostAddress( columnFamilyResult.getString( "postAddress" ) );
1200             mailingList.setSubscribeAddress( columnFamilyResult.getString( "subscribeAddress" ) );
1201             mailingList.setUnsubscribeAddress( columnFamilyResult.getString( "unsubscribeAddress" ) );
1202
1203             List<String> otherArchives = new ArrayList<>();
1204
1205             for ( String columnName : columnFamilyResult.getColumnNames() )
1206             {
1207                 if ( StringUtils.startsWith( columnName, "otherArchive." ) )
1208                 {
1209                     otherArchives.add( columnFamilyResult.getString( columnName ) );
1210                 }
1211             }
1212
1213             mailingList.setOtherArchives( otherArchives );
1214             mailingLists.add( mailingList );
1215         }
1216
1217         return mailingLists;
1218     }
1219
1220     protected void recordLicenses( String projectVersionMetadataKey, List<License> licenses )
1221     {
1222
1223         if ( licenses == null || licenses.isEmpty() )
1224         {
1225             return;
1226         }
1227         Mutator<String> licenseMutator = this.licenseTemplate.createMutator();
1228
1229         for ( License license : licenses )
1230         {
1231             // we don't care about the key as the real used one with the projectVersionMetadata
1232             String keyLicense = UUID.randomUUID().toString();
1233             String cfLicense = cassandraArchivaManager.getLicenseFamilyName();
1234
1235             addInsertion( licenseMutator, keyLicense, cfLicense, "projectVersionMetadataModel.key",
1236                           projectVersionMetadataKey );
1237
1238             addInsertion( licenseMutator, keyLicense, cfLicense, NAME.toString(), license.getName() );
1239
1240             addInsertion( licenseMutator, keyLicense, cfLicense, URL.toString(), license.getUrl() );
1241
1242         }
1243         licenseMutator.execute();
1244     }
1245
1246     protected void removeLicenses( String projectVersionMetadataKey )
1247     {
1248
1249         QueryResult<OrderedRows<String, String, String>> result =
1250             HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1251                 .setColumnFamily( cassandraArchivaManager.getLicenseFamilyName() ) //
1252                 .setColumnNames( NAME.toString() ) //
1253                 .setRowCount( Integer.MAX_VALUE ) //
1254                 .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1255                 .execute();
1256         for ( Row<String, String, String> row : result.get() )
1257         {
1258             this.licenseTemplate.deleteRow( row.getKey() );
1259         }
1260     }
1261
1262     protected List<License> getLicenses( String projectVersionMetadataKey )
1263     {
1264         List<License> licenses = new ArrayList<>();
1265
1266         QueryResult<OrderedRows<String, String, String>> result =
1267             HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1268                 .setColumnFamily( cassandraArchivaManager.getLicenseFamilyName() ) //
1269                 .setColumnNames( "projectVersionMetadataModel.key" ) //
1270                 .setRowCount( Integer.MAX_VALUE ) //
1271                 .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1272                 .execute();
1273
1274         for ( Row<String, String, String> row : result.get() )
1275         {
1276             ColumnFamilyResult<String, String> columnFamilyResult = this.licenseTemplate.queryColumns( row.getKey() );
1277
1278             licenses.add(
1279                 new License( columnFamilyResult.getString( NAME.toString() ), columnFamilyResult.getString( URL.toString() ) ) );
1280         }
1281
1282         return licenses;
1283     }
1284
1285
1286     protected void recordDependencies( String projectVersionMetadataKey, List<Dependency> dependencies,
1287                                        String repositoryId )
1288     {
1289
1290         if ( dependencies == null || dependencies.isEmpty() )
1291         {
1292             return;
1293         }
1294         Mutator<String> dependencyMutator = this.dependencyTemplate.createMutator();
1295
1296         for ( Dependency dependency : dependencies )
1297         {
1298             // we don't care about the key as the real used one with the projectVersionMetadata
1299             String keyDependency = UUID.randomUUID().toString();
1300             String cfDependency = cassandraArchivaManager.getDependencyFamilyName();
1301
1302             addInsertion( dependencyMutator, keyDependency, cfDependency, "projectVersionMetadataModel.key",
1303                           projectVersionMetadataKey );
1304
1305             addInsertion( dependencyMutator, keyDependency, cfDependency, REPOSITORY_NAME.toString(), repositoryId );
1306
1307             addInsertion( dependencyMutator, keyDependency, cfDependency, "classifier", dependency.getClassifier() );
1308
1309             addInsertion( dependencyMutator, keyDependency, cfDependency, "optional",
1310                           Boolean.toString( dependency.isOptional() ) );
1311
1312             addInsertion( dependencyMutator, keyDependency, cfDependency, "scope", dependency.getScope() );
1313
1314             addInsertion( dependencyMutator, keyDependency, cfDependency, "systemPath", dependency.getSystemPath() );
1315
1316             addInsertion( dependencyMutator, keyDependency, cfDependency, "type", dependency.getType() );
1317
1318             addInsertion( dependencyMutator, keyDependency, cfDependency, ARTIFACT_ID.toString(), dependency.getArtifactId() );
1319
1320             addInsertion( dependencyMutator, keyDependency, cfDependency, GROUP_ID.toString(), dependency.getGroupId() );
1321
1322             addInsertion( dependencyMutator, keyDependency, cfDependency, VERSION.toString(), dependency.getVersion() );
1323
1324         }
1325         dependencyMutator.execute();
1326     }
1327
1328     protected void removeDependencies( String projectVersionMetadataKey )
1329     {
1330
1331         QueryResult<OrderedRows<String, String, String>> result =
1332             HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1333                 .setColumnFamily( cassandraArchivaManager.getDependencyFamilyName() ) //
1334                 .setColumnNames( GROUP_ID.toString() ) //
1335                 .setRowCount( Integer.MAX_VALUE ) //
1336                 .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1337                 .execute();
1338         for ( Row<String, String, String> row : result.get() )
1339         {
1340             this.dependencyTemplate.deleteRow( row.getKey() );
1341         }
1342     }
1343
1344     protected List<Dependency> getDependencies( String projectVersionMetadataKey )
1345     {
1346         List<Dependency> dependencies = new ArrayList<>();
1347
1348         QueryResult<OrderedRows<String, String, String>> result =
1349             HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1350                 .setColumnFamily( cassandraArchivaManager.getDependencyFamilyName() ) //
1351                 .setColumnNames( "projectVersionMetadataModel.key" ) //
1352                 .setRowCount( Integer.MAX_VALUE ) //
1353                 .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1354                 .execute();
1355
1356         for ( Row<String, String, String> row : result.get() )
1357         {
1358             ColumnFamilyResult<String, String> columnFamilyResult =
1359                 this.dependencyTemplate.queryColumns( row.getKey() );
1360
1361             Dependency dependency = new Dependency();
1362             dependency.setClassifier( columnFamilyResult.getString( "classifier" ) );
1363
1364             dependency.setOptional( Boolean.parseBoolean( columnFamilyResult.getString( "optional" ) ) );
1365
1366             dependency.setScope( columnFamilyResult.getString( "scope" ) );
1367
1368             dependency.setSystemPath( columnFamilyResult.getString( "systemPath" ) );
1369
1370             dependency.setType( columnFamilyResult.getString( "type" ) );
1371
1372             dependency.setArtifactId( columnFamilyResult.getString( ARTIFACT_ID.toString() ) );
1373
1374             dependency.setGroupId( columnFamilyResult.getString( GROUP_ID.toString() ) );
1375
1376             dependency.setVersion( columnFamilyResult.getString( VERSION.toString() ) );
1377
1378             dependencies.add( dependency );
1379         }
1380
1381         return dependencies;
1382     }
1383
1384     private Map<String, String> mapChecksums(Map<ChecksumAlgorithm,String> checksums) {
1385         return checksums.entrySet().stream().collect(Collectors.toMap(
1386                 e -> e.getKey().name(), e -> e.getValue()
1387         ));
1388     }
1389
1390     private Map<ChecksumAlgorithm, String> mapChecksumsReverse(Map<String,String> checksums) {
1391         return checksums.entrySet().stream().collect(Collectors.toMap(
1392                 e -> ChecksumAlgorithm.valueOf(e.getKey()), e -> e.getValue()
1393         ));
1394     }
1395
1396     @Override
1397     public void updateArtifact( RepositorySession session, String repositoryId, String namespaceId, String projectId, String projectVersion,
1398                                 ArtifactMetadata artifactMeta )
1399         throws MetadataRepositoryException
1400     {
1401
1402         Namespace namespace = getNamespace( repositoryId, namespaceId );
1403         if ( namespace == null )
1404         {
1405             namespace = updateOrAddNamespace( repositoryId, namespaceId );
1406         }
1407
1408         ProjectMetadata projectMetadata = new ProjectMetadata();
1409         projectMetadata.setId( projectId );
1410         projectMetadata.setNamespace( namespaceId );
1411         updateProject( session, repositoryId, projectMetadata );
1412
1413         String key = new ArtifactMetadataModel.KeyBuilder().withNamespace( namespace ).withProject( projectId ).withId(
1414             artifactMeta.getId() ).withProjectVersion( projectVersion ).build();
1415
1416         // exists?
1417
1418         boolean exists = this.artifactMetadataTemplate.isColumnsExist( key );
1419
1420         if ( exists )
1421         {
1422             // updater
1423             ColumnFamilyUpdater<String, String> updater = this.artifactMetadataTemplate.createUpdater( key );
1424             updater.setLong( FILE_LAST_MODIFIED.toString(), artifactMeta.getFileLastModified().toInstant().toEpochMilli());
1425             updater.setLong( WHEN_GATHERED.toString(), artifactMeta.getWhenGathered().toInstant().toEpochMilli() );
1426             updater.setLong( SIZE.toString(), artifactMeta.getSize() );
1427             addUpdateStringValue( updater, VERSION.toString(), artifactMeta.getVersion() );
1428             removeChecksums(key);
1429             recordChecksums(repositoryId, key, mapChecksums(artifactMeta.getChecksums()));
1430             this.artifactMetadataTemplate.update( updater );
1431         }
1432         else
1433         {
1434             String cf = this.cassandraArchivaManager.getArtifactMetadataFamilyName();
1435             // create
1436             this.artifactMetadataTemplate.createMutator() //
1437                 .addInsertion( key, cf, column( ID.toString(), artifactMeta.getId() ) )//
1438                 .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
1439                 .addInsertion( key, cf, column( NAMESPACE_ID.toString(), namespaceId ) ) //
1440                 .addInsertion( key, cf, column( PROJECT.toString(), artifactMeta.getProject() ) ) //
1441                 .addInsertion( key, cf, column( PROJECT_VERSION.toString(), projectVersion ) ) //
1442                 .addInsertion( key, cf, column( VERSION.toString(), artifactMeta.getVersion() ) ) //
1443                 .addInsertion( key, cf, column( FILE_LAST_MODIFIED.toString(), artifactMeta.getFileLastModified().toInstant().toEpochMilli() ) ) //
1444                 .addInsertion( key, cf, column( SIZE.toString(), artifactMeta.getSize() ) ) //
1445                 .addInsertion( key, cf, column( WHEN_GATHERED.toString(), artifactMeta.getWhenGathered().toInstant().toEpochMilli() ) )//
1446                 .execute();
1447             recordChecksums(repositoryId, key, mapChecksums(artifactMeta.getChecksums()));
1448         }
1449
1450         key = new ProjectVersionMetadataModel.KeyBuilder() //
1451             .withRepository( repositoryId ) //
1452             .withNamespace( namespace ) //
1453             .withProjectId( projectId ) //
1454             .withProjectVersion( projectVersion ) //
1455             .withId( artifactMeta.getId() ) //
1456             .build();
1457
1458         QueryResult<OrderedRows<String, String, String>> result = HFactory //
1459             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1460             .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
1461             .setColumnNames( VERSION.toString() ) //
1462             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1463             .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
1464             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
1465             .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
1466             .addEqualsExpression( VERSION.toString(), artifactMeta.getVersion() ) //
1467             .execute();
1468
1469         exists = result.get().getCount() > 0;
1470
1471         if ( !exists )
1472         {
1473             String cf = this.cassandraArchivaManager.getProjectVersionMetadataFamilyName();
1474
1475             projectVersionMetadataTemplate.createMutator() //
1476                 .addInsertion( key, cf, column( NAMESPACE_ID.toString(), namespace.getName() ) ) //
1477                 .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
1478                 .addInsertion( key, cf, column( PROJECT_VERSION.toString(), projectVersion ) ) //
1479                 .addInsertion( key, cf, column( PROJECT_ID.toString(), projectId ) ) //
1480                 .addInsertion( key, cf, column( VERSION.toString(), artifactMeta.getVersion() ) ) //
1481                 .execute();
1482
1483         }
1484
1485         ArtifactMetadataModel artifactMetadataModel = new ArtifactMetadataModel();
1486
1487         artifactMetadataModel.setRepositoryId( repositoryId );
1488         artifactMetadataModel.setNamespace( namespaceId );
1489         artifactMetadataModel.setProject( projectId );
1490         artifactMetadataModel.setProjectVersion( projectVersion );
1491         artifactMetadataModel.setVersion( artifactMeta.getVersion() );
1492         artifactMetadataModel.setFileLastModified( artifactMeta.getFileLastModified() == null
1493                                                        ? ZonedDateTime.now().toInstant().toEpochMilli()
1494                                                        : artifactMeta.getFileLastModified().toInstant().toEpochMilli() );
1495         artifactMetadataModel.setChecksums(mapChecksums(artifactMeta.getChecksums()));
1496
1497         // now facets
1498         updateFacets( artifactMeta, artifactMetadataModel );
1499
1500     }
1501
1502     @Override
1503     public Collection<String> getArtifactVersions( RepositorySession session, final String repoId, final String namespace, final String projectId,
1504                                                    final String projectVersion )
1505         throws MetadataResolutionException
1506     {
1507
1508         QueryResult<OrderedRows<String, String, String>> result = HFactory //
1509             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1510             .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
1511             .setColumnNames( VERSION.toString() ) //
1512             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
1513             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
1514             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
1515             .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
1516             .execute();
1517
1518         final Set<String> versions = new HashSet<>();
1519
1520         for ( Row<String, String, String> row : result.get() )
1521         {
1522             versions.add( getStringValue( row.getColumnSlice(), VERSION.toString() ) );
1523         }
1524
1525         return versions;
1526
1527     }
1528
1529     /**
1530      * iterate over available facets to remove/add from the artifactMetadata
1531      *
1532      * @param facetedMetadata
1533      * @param artifactMetadataModel only use for the key
1534      */
1535     private void updateFacets( final FacetedMetadata facetedMetadata,
1536                                final ArtifactMetadataModel artifactMetadataModel )
1537     {
1538
1539         String cf = cassandraArchivaManager.getMetadataFacetFamilyName();
1540
1541         for ( final String facetId : getSupportedFacets() )
1542         {
1543             MetadataFacet metadataFacet = facetedMetadata.getFacet( facetId );
1544             if ( metadataFacet == null )
1545             {
1546                 continue;
1547             }
1548             // clean first
1549
1550             QueryResult<OrderedRows<String, String, String>> result =
1551                 HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1552                     .setColumnFamily( cf ) //
1553                     .setColumnNames( REPOSITORY_NAME.toString() ) //
1554                     .addEqualsExpression( REPOSITORY_NAME.toString(), artifactMetadataModel.getRepositoryId() ) //
1555                     .addEqualsExpression( NAMESPACE_ID.toString(), artifactMetadataModel.getNamespace() ) //
1556                     .addEqualsExpression( PROJECT_ID.toString(), artifactMetadataModel.getProject() ) //
1557                     .addEqualsExpression( PROJECT_VERSION.toString(), artifactMetadataModel.getProjectVersion() ) //
1558                     .addEqualsExpression( FACET_ID.toString(), facetId ) //
1559                     .execute();
1560
1561             for ( Row<String, String, String> row : result.get().getList() )
1562             {
1563                 this.metadataFacetTemplate.deleteRow( row.getKey() );
1564             }
1565
1566             Map<String, String> properties = metadataFacet.toProperties();
1567
1568             for ( Map.Entry<String, String> entry : properties.entrySet() )
1569             {
1570                 String key = new MetadataFacetModel.KeyBuilder().withKey( entry.getKey() ).withArtifactMetadataModel(
1571                     artifactMetadataModel ).withFacetId( facetId ).withName( metadataFacet.getName() ).build();
1572                 Mutator<String> mutator = metadataFacetTemplate.createMutator() //
1573                     .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), artifactMetadataModel.getRepositoryId() ) ) //
1574                     .addInsertion( key, cf, column( NAMESPACE_ID.toString(), artifactMetadataModel.getNamespace() ) ) //
1575                     .addInsertion( key, cf, column( PROJECT_ID.toString(), artifactMetadataModel.getProject() ) ) //
1576                     .addInsertion( key, cf, column( PROJECT_VERSION.toString(), artifactMetadataModel.getProjectVersion() ) ) //
1577                     .addInsertion( key, cf, column( FACET_ID.toString(), facetId ) ) //
1578                     .addInsertion( key, cf, column( KEY.toString(), entry.getKey() ) ) //
1579                     .addInsertion( key, cf, column( VALUE.toString(), entry.getValue() ) );
1580
1581                 if ( metadataFacet.getName() != null )
1582                 {
1583                     mutator.addInsertion( key, cf, column( NAME.toString(), metadataFacet.getName() ) );
1584                 }
1585
1586                 mutator.execute();
1587             }
1588         }
1589     }
1590
1591
1592     @Override
1593     public List<String> getMetadataFacets( RepositorySession session, final String repositoryId, final String facetId )
1594         throws MetadataRepositoryException
1595     {
1596
1597         QueryResult<OrderedRows<String, String, String>> result = HFactory //
1598             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1599             .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1600             .setColumnNames( NAME.toString() ) //
1601             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1602             .addEqualsExpression( FACET_ID.toString(), facetId ) //
1603             .execute();
1604
1605         final List<String> facets = new ArrayList<>();
1606
1607         for ( Row<String, String, String> row : result.get() )
1608         {
1609             facets.add( getStringValue( row.getColumnSlice(), NAME.toString() ) );
1610         }
1611         return facets;
1612     }
1613
1614     private <T> Spliterator<T> createResultSpliterator( QueryResult<OrderedRows<String, String, String>> result, BiFunction<Row<String, String, String>, T, T> converter) throws MetadataRepositoryException
1615     {
1616         final int size = result.get().getCount();
1617         final Iterator<Row<String, String, String>> it = result.get( ).iterator( );
1618
1619         return new Spliterator<T>( )
1620         {
1621             private T lastItem = null;
1622
1623             @Override
1624             public boolean tryAdvance( Consumer<? super T> action )
1625             {
1626                 if (size>=1)
1627                 {
1628                     if(it.hasNext())
1629                     {
1630                         while ( it.hasNext( ) )
1631                         {
1632                             Row<String, String, String> row = it.next( );
1633                             T item = converter.apply( row, lastItem );
1634                             if ( item != null && lastItem !=null && item != lastItem )
1635                             {
1636                                 action.accept( lastItem );
1637                                 lastItem = item;
1638                                 return true;
1639                             }
1640                             lastItem = item;
1641                         }
1642                         action.accept( lastItem );
1643                         return true;
1644                     } else {
1645                         return false;
1646                     }
1647                 }
1648                 return false;
1649             }
1650
1651             @Override
1652             public Spliterator<T> trySplit( )
1653             {
1654                 return null;
1655             }
1656
1657             @Override
1658             public long estimateSize( )
1659             {
1660                 return size;
1661             }
1662
1663             @Override
1664             public int characteristics( )
1665             {
1666                 return ORDERED+NONNULL+SIZED;
1667             }
1668         };
1669     }
1670
1671
1672     /**
1673      * Implementation is not very performant, because sorting is part of the stream. I do not know how to specify the sort
1674      * in the query.
1675      * 
1676      * @param <T>
1677      * @param session
1678      * @param repositoryId
1679      * @param facetClazz
1680      * @param queryParameter
1681      * @return
1682      * @throws MetadataRepositoryException
1683      */
1684     @Override
1685     public <T extends MetadataFacet> Stream<T> getMetadataFacetStream(RepositorySession session, String repositoryId, Class<T> facetClazz, QueryParameter queryParameter) throws MetadataRepositoryException
1686     {
1687         final MetadataFacetFactory<T> metadataFacetFactory = getFacetFactory( facetClazz );
1688         final String facetId = metadataFacetFactory.getFacetId( );
1689
1690         QueryResult<OrderedRows<String, String, String>> result = HFactory //
1691             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1692             .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName( ) ) //
1693             .setColumnNames( NAME.toString( ), KEY.toString( ), VALUE.toString( ) ) //
1694             .addEqualsExpression( REPOSITORY_NAME.toString( ), repositoryId ) //
1695             .addEqualsExpression( FACET_ID.toString( ), facetId ) //
1696             .setRange( null, null, false, Integer.MAX_VALUE )
1697             .setRowCount( Integer.MAX_VALUE )
1698             .execute( );
1699
1700
1701
1702         return StreamSupport.stream( createResultSpliterator( result, ( Row<String, String, String> row, T lastItem)-> {
1703             ColumnSlice<String, String> columnSlice = row.getColumnSlice();
1704             String name = getStringValue( columnSlice, NAME.toString( ) );
1705             T updateItem;
1706             if (lastItem!=null && lastItem.getName().equals(name))
1707             {
1708                 updateItem = lastItem;
1709             } else
1710             {
1711                 updateItem = metadataFacetFactory.createMetadataFacet( repositoryId, name );
1712             }
1713             String key = getStringValue( columnSlice, KEY.toString() );
1714             if (StringUtils.isNotEmpty( key ))
1715             {
1716                 Map<String, String> map = new HashMap<>( );
1717                 map.put( key , getStringValue( columnSlice, VALUE.toString( ) ) );
1718                 updateItem.fromProperties( map );
1719             }
1720             return updateItem;
1721
1722         }), false ).sorted( (f1, f2) -> f1.getName()!=null ? f1.getName().compareTo( f2.getName() ) : 1 ).skip( queryParameter.getOffset()).limit( queryParameter.getLimit());
1723     }
1724
1725     @Override
1726     public boolean hasMetadataFacet( RepositorySession session, String repositoryId, String facetId )
1727         throws MetadataRepositoryException
1728     {
1729         return !getMetadataFacets( session, repositoryId, facetId ).isEmpty();
1730     }
1731
1732     @Override
1733     public <T extends MetadataFacet> T getMetadataFacet( RepositorySession session, final String repositoryId, final Class<T> facetClazz, final String name )
1734         throws MetadataRepositoryException
1735     {
1736         final MetadataFacetFactory<T> metadataFacetFactory = getFacetFactory( facetClazz );
1737         if (metadataFacetFactory==null) {
1738             return null;
1739         }
1740         final String facetId = metadataFacetFactory.getFacetId( );
1741         if ( metadataFacetFactory == null )
1742         {
1743             return null;
1744         }
1745
1746         QueryResult<OrderedRows<String, String, String>> result = HFactory //
1747             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1748             .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1749             .setColumnNames( KEY.toString(), VALUE.toString() ) //
1750             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1751             .addEqualsExpression( FACET_ID.toString(), facetId ) //
1752             .addEqualsExpression( NAME.toString(), name ) //
1753             .execute();
1754
1755         T metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
1756         int size = result.get().getCount();
1757         if ( size < 1 )
1758         {
1759             return null;
1760         }
1761         Map<String, String> map = new HashMap<>( size );
1762         for ( Row<String, String, String> row : result.get() )
1763         {
1764             ColumnSlice<String, String> columnSlice = row.getColumnSlice();
1765             map.put( getStringValue( columnSlice, KEY.toString() ), getStringValue( columnSlice, VALUE.toString() ) );
1766         }
1767         metadataFacet.fromProperties( map );
1768         return metadataFacet;
1769     }
1770
1771     @Override
1772     public MetadataFacet getMetadataFacet( RepositorySession session, String repositoryId, String facetId, String name ) throws MetadataRepositoryException
1773     {
1774         return getMetadataFacet( session, repositoryId, getFactoryClassForId( facetId ), name );
1775     }
1776
1777     @Override
1778     public void addMetadataFacet( RepositorySession session, String repositoryId, MetadataFacet metadataFacet )
1779         throws MetadataRepositoryException
1780     {
1781
1782         if ( metadataFacet == null )
1783         {
1784             return;
1785         }
1786
1787         if ( metadataFacet.toProperties().isEmpty() )
1788         {
1789             String key = new MetadataFacetModel.KeyBuilder().withRepositoryId( repositoryId ).withFacetId(
1790                 metadataFacet.getFacetId() ).withName( metadataFacet.getName() ).build();
1791
1792             boolean exists = this.metadataFacetTemplate.isColumnsExist( key );
1793
1794             if ( exists )
1795             {
1796                 ColumnFamilyUpdater<String, String> updater = this.metadataFacetTemplate.createUpdater( key );
1797                 addUpdateStringValue( updater, FACET_ID.toString(), metadataFacet.getFacetId() );
1798                 addUpdateStringValue( updater, NAME.toString(), metadataFacet.getName() );
1799                 this.metadataFacetTemplate.update( updater );
1800             }
1801             else
1802             {
1803                 String cf = this.cassandraArchivaManager.getMetadataFacetFamilyName();
1804                 this.metadataFacetTemplate.createMutator() //
1805                     .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
1806                     .addInsertion( key, cf, column( FACET_ID.toString(), metadataFacet.getFacetId() ) ) //
1807                     .addInsertion( key, cf, column( NAME.toString(), metadataFacet.getName() ) ) //
1808                     .execute();
1809             }
1810
1811         }
1812         else
1813         {
1814             for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
1815             {
1816                 String key = new MetadataFacetModel.KeyBuilder().withRepositoryId( repositoryId ).withFacetId(
1817                     metadataFacet.getFacetId() ).withName( metadataFacet.getName() ).withKey( entry.getKey() ).build();
1818
1819                 boolean exists = this.metadataFacetTemplate.isColumnsExist( key );
1820                 if ( !exists )
1821                 {
1822                     String cf = this.cassandraArchivaManager.getMetadataFacetFamilyName();
1823                     this.metadataFacetTemplate.createMutator() //
1824                         .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
1825                         .addInsertion( key, cf, column( FACET_ID.toString(), metadataFacet.getFacetId() ) ) //
1826                         .addInsertion( key, cf, column( NAME.toString(), metadataFacet.getName() ) ) //
1827                         .addInsertion( key, cf, column( KEY.toString(), entry.getKey() ) ) //
1828                         .addInsertion( key, cf, column( VALUE.toString(), entry.getValue() ) ) //
1829                         .execute();
1830                 }
1831                 else
1832                 {
1833                     ColumnFamilyUpdater<String, String> updater = this.metadataFacetTemplate.createUpdater( key );
1834                     addUpdateStringValue( updater, VALUE.toString(), entry.getValue() );
1835                     this.metadataFacetTemplate.update( updater );
1836                 }
1837             }
1838         }
1839     }
1840
1841     @Override
1842     public void removeMetadataFacets( RepositorySession session, final String repositoryId, final String facetId )
1843         throws MetadataRepositoryException
1844     {
1845
1846         QueryResult<OrderedRows<String, String, String>> result = HFactory //
1847             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1848             .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1849             .setColumnNames( KEY.toString(), VALUE.toString() ) //
1850             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1851             .addEqualsExpression( FACET_ID.toString(), facetId ) //
1852             .execute();
1853
1854         for ( Row<String, String, String> row : result.get() )
1855         {
1856             this.metadataFacetTemplate.deleteRow( row.getKey() );
1857         }
1858
1859     }
1860
1861     @Override
1862     public void removeMetadataFacet( RepositorySession session, final String repositoryId, final String facetId, final String name )
1863         throws MetadataRepositoryException
1864     {
1865
1866         QueryResult<OrderedRows<String, String, String>> result = HFactory //
1867             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1868             .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1869             .setColumnNames( KEY.toString(), VALUE.toString() ) //
1870             .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1871             .addEqualsExpression( FACET_ID.toString(), facetId ) //
1872             .addEqualsExpression( NAME.toString(), name ) //
1873             .execute();
1874
1875         for ( Row<String, String, String> row : result.get() )
1876         {
1877             this.metadataFacetTemplate.deleteRow( row.getKey() );
1878         }
1879     }
1880
1881     @Override
1882     public List<ArtifactMetadata> getArtifactsByDateRange( RepositorySession session, final String repositoryId, final ZonedDateTime startTime,
1883                                                            final ZonedDateTime endTime, QueryParameter queryParameter )
1884         throws MetadataRepositoryException
1885     {
1886
1887         LongSerializer ls = LongSerializer.get();
1888         RangeSlicesQuery<String, String, Long> query = HFactory //
1889             .createRangeSlicesQuery( keyspace, ss, ss, ls ) //
1890             .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
1891             .setColumnNames( ArtifactMetadataModel.COLUMNS ); //
1892
1893
1894         if ( startTime != null )
1895         {
1896             query = query.addGteExpression( WHEN_GATHERED.toString(), startTime.toInstant().toEpochMilli() );
1897         }
1898         if ( endTime != null )
1899         {
1900             query = query.addLteExpression( WHEN_GATHERED.toString(), endTime.toInstant().toEpochMilli() );
1901         }
1902         QueryResult<OrderedRows<String, String, Long>> result = query.execute();
1903
1904         List<ArtifactMetadata> artifactMetadatas = new ArrayList<>( result.get().getCount() );
1905         Iterator<Row<String, String, Long>> keyIter = result.get().iterator();
1906         if (keyIter.hasNext()) {
1907             String key = keyIter.next().getKey();
1908             for (Row<String, String, Long> row : result.get()) {
1909                 ColumnSlice<String, Long> columnSlice = row.getColumnSlice();
1910                 String repositoryName = getAsStringValue(columnSlice, REPOSITORY_NAME.toString());
1911                 if (StringUtils.equals(repositoryName, repositoryId)) {
1912
1913                     artifactMetadatas.add(mapArtifactMetadataLongColumnSlice(key, columnSlice));
1914                 }
1915             }
1916         }
1917
1918         return artifactMetadatas;
1919     }
1920
1921     /**
1922      * For documentation see {@link MetadataRepository#getArtifactByDateRangeStream(RepositorySession, String, ZonedDateTime, ZonedDateTime, QueryParameter)}
1923      *
1924      * This implementation orders the stream. It does not order the query in the backend.
1925      *
1926      * @param session The repository session
1927      * @param repositoryId The repository id
1928      * @param startTime The start time, can be <code>null</code>
1929      * @param endTime The end time, can be <code>null</code>
1930      * @param queryParameter Additional parameters for the query that affect ordering and number of returned results.
1931      * @return
1932      * @throws MetadataRepositoryException
1933      * @see MetadataRepository#getArtifactByDateRangeStream
1934      */
1935     @Override
1936     public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
1937     {
1938         Comparator<ArtifactMetadata> comp = getArtifactMetadataComparator(queryParameter, "whenGathered");
1939         return getArtifactsByDateRange(session, repositoryId, startTime, endTime, queryParameter).stream().sorted(comp).skip(queryParameter.getOffset()).limit(queryParameter.getLimit());
1940     }
1941
1942
1943     protected ArtifactMetadata mapArtifactMetadataLongColumnSlice( String key, ColumnSlice<String, Long> columnSlice )
1944     {
1945         ArtifactMetadata artifactMetadata = new ArtifactMetadata();
1946         artifactMetadata.setNamespace( getAsStringValue( columnSlice, NAMESPACE_ID.toString() ) );
1947         artifactMetadata.setSize( getLongValue( columnSlice, SIZE.toString() ) );
1948         artifactMetadata.setId( getAsStringValue( columnSlice, ID.toString() ) );
1949         artifactMetadata.setFileLastModified( getLongValue( columnSlice, FILE_LAST_MODIFIED.toString() ) );
1950         artifactMetadata.setMd5( getAsStringValue( columnSlice, MD5.toString() ) );
1951         artifactMetadata.setProject( getAsStringValue( columnSlice, PROJECT.toString() ) );
1952         artifactMetadata.setProjectVersion( getAsStringValue( columnSlice, PROJECT_VERSION.toString() ) );
1953         artifactMetadata.setRepositoryId( getAsStringValue( columnSlice, REPOSITORY_NAME.toString() ) );
1954         artifactMetadata.setSha1( getAsStringValue( columnSlice, SHA1.toString() ) );
1955         artifactMetadata.setVersion( getAsStringValue( columnSlice, VERSION.toString() ) );
1956         Long whenGathered = getLongValue( columnSlice, WHEN_GATHERED.toString() );
1957         if ( whenGathered != null )
1958         {
1959             artifactMetadata.setWhenGathered(ZonedDateTime.ofInstant(Instant.ofEpochMilli(whenGathered), STORAGE_TZ));
1960         }
1961         artifactMetadata.setChecksums(mapChecksumsReverse(getChecksums(key)));
1962         return artifactMetadata;
1963     }
1964
1965     protected ArtifactMetadata mapArtifactMetadataStringColumnSlice( String key, ColumnSlice<String, String> columnSlice )
1966     {
1967         ArtifactMetadata artifactMetadata = new ArtifactMetadata();
1968         artifactMetadata.setNamespace( getStringValue( columnSlice, NAMESPACE_ID.toString() ) );
1969         artifactMetadata.setSize( getAsLongValue( columnSlice, SIZE.toString() ) );
1970         artifactMetadata.setId( getStringValue( columnSlice, ID.toString() ) );
1971         artifactMetadata.setFileLastModified( getAsLongValue( columnSlice, FILE_LAST_MODIFIED.toString() ) );
1972         artifactMetadata.setMd5( getStringValue( columnSlice, MD5.toString() ) );
1973         artifactMetadata.setProject( getStringValue( columnSlice, PROJECT.toString() ) );
1974         artifactMetadata.setProjectVersion( getStringValue( columnSlice, PROJECT_VERSION.toString() ) );
1975         artifactMetadata.setRepositoryId( getStringValue( columnSlice, REPOSITORY_NAME.toString() ) );
1976         artifactMetadata.setSha1( getStringValue( columnSlice, SHA1.toString() ) );
1977         artifactMetadata.setVersion( getStringValue( columnSlice, VERSION.toString() ) );
1978         Long whenGathered = getAsLongValue( columnSlice, WHEN_GATHERED.toString() );
1979         if ( whenGathered != null )
1980         {
1981             artifactMetadata.setWhenGathered(ZonedDateTime.ofInstant(Instant.ofEpochMilli(whenGathered), STORAGE_TZ));
1982         }
1983         artifactMetadata.setChecksums(mapChecksumsReverse(getChecksums(key)));
1984         return artifactMetadata;
1985     }
1986
1987     @Override
1988     public List<ArtifactMetadata> getArtifactsByChecksum(RepositorySession session, final String repositoryId, final String checksum )
1989         throws MetadataRepositoryException
1990     {
1991
1992         // cql cannot run or in queries so running twice the query
1993         Map<String, ArtifactMetadata> artifactMetadataMap = new HashMap<>();
1994
1995         RangeSlicesQuery<String, String, String> query = HFactory //
1996             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1997             .setColumnFamily( cassandraArchivaManager.getChecksumFamilyName()) //
1998             .setColumnNames(ARTIFACT_METADATA_MODEL_KEY); //
1999
2000         query = query.addEqualsExpression( CHECKSUM_VALUE.toString(), checksum )
2001                 .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId );
2002
2003         QueryResult<OrderedRows<String, String, String>> result = query.execute();
2004
2005         List<String> artifactKeys = new ArrayList<>();
2006         for ( Row<String, String, String> row : result.get() )
2007         {
2008             ColumnSlice<String, String> columnSlice = row.getColumnSlice();
2009
2010             artifactKeys.add(columnSlice.getColumnByName(ARTIFACT_METADATA_MODEL_KEY).getValue());
2011
2012         }
2013
2014         for (String key : artifactKeys) {
2015             query = HFactory //
2016                     .createRangeSlicesQuery(keyspace, ss, ss, ss) //
2017                     .setColumnFamily(cassandraArchivaManager.getArtifactMetadataFamilyName()) //
2018                     .setColumnNames(NAMESPACE_ID.toString(), SIZE.toString(), ID.toString(), FILE_LAST_MODIFIED.toString(), MD5.toString(), PROJECT.toString(), PROJECT_VERSION.toString(),
2019                             REPOSITORY_NAME.toString(), VERSION.toString(), WHEN_GATHERED.toString(), SHA1.toString())
2020                     .setKeys(key, key);
2021             result = query.execute();
2022
2023             for (Row<String, String, String> row : result.get()) {
2024                 ColumnSlice<String, String> columnSlice = row.getColumnSlice();
2025
2026                 artifactMetadataMap.put(row.getKey(), mapArtifactMetadataStringColumnSlice(key, columnSlice));
2027             }
2028         }
2029
2030         return new ArrayList(artifactMetadataMap.values());
2031     }
2032
2033     /**
2034      * Project version and artifact level metadata are stored in the same place, no distinctions in Cassandra
2035      * implementation, just calls {@link MetadataRepository#getArtifactsByAttribute(RepositorySession, String, String, String)}
2036      */
2037     @Override
2038     public List<ArtifactMetadata> getArtifactsByProjectVersionFacet( RepositorySession session, String key, String value, String repositoryId )
2039         throws MetadataRepositoryException
2040     {
2041         return this.getArtifactsByAttribute( session, key, value, repositoryId );
2042     }
2043
2044     @Override
2045     public List<ArtifactMetadata> getArtifactsByAttribute( RepositorySession session, String key, String value, String repositoryId )
2046         throws MetadataRepositoryException
2047     {
2048         RangeSlicesQuery<String, String, String> query =
2049             HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2050             .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
2051             .setColumnNames( MetadataFacetModel.COLUMNS ) //
2052             .addEqualsExpression( VALUE.toString(), value );
2053
2054         if ( key != null )
2055         {
2056             query.addEqualsExpression( KEY.toString(), key ); //
2057         }
2058         if ( repositoryId != null )
2059         {
2060             query.addEqualsExpression( "repositoryName", repositoryId );
2061         }
2062
2063         QueryResult<OrderedRows<String, String, String>> metadataFacetResult = query.execute();
2064         if ( metadataFacetResult.get() == null || metadataFacetResult.get().getCount() < 1 )
2065         {
2066             return Collections.emptyList();
2067         }
2068
2069         List<ArtifactMetadata> artifactMetadatas = new LinkedList<ArtifactMetadata>();
2070
2071         // TODO doing multiple queries, there should be a way to get all the artifactMetadatas for any number of
2072         // projects
2073         for ( Row<String, String, String> row : metadataFacetResult.get() )
2074         {
2075             QueryResult<OrderedRows<String, String, String>> artifactMetadataResult =
2076                 HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2077                 .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2078                 .setColumnNames( ArtifactMetadataModel.COLUMNS ) //
2079                 .setRowCount( Integer.MAX_VALUE ) //
2080                 .addEqualsExpression( REPOSITORY_NAME.toString(),
2081                                       getStringValue( row.getColumnSlice(), REPOSITORY_NAME ) ) //
2082                 .addEqualsExpression( NAMESPACE_ID.toString(), getStringValue( row.getColumnSlice(), NAMESPACE_ID ) ) //
2083                 .addEqualsExpression( PROJECT.toString(), getStringValue( row.getColumnSlice(), PROJECT_ID ) ) //
2084                 .addEqualsExpression( PROJECT_VERSION.toString(),
2085                                       getStringValue( row.getColumnSlice(), PROJECT_VERSION ) ) //
2086                 .execute();
2087
2088             if ( artifactMetadataResult.get() == null || artifactMetadataResult.get().getCount() < 1 )
2089             {
2090                 return Collections.emptyList();
2091             }
2092
2093             for ( Row<String, String, String> artifactMetadataRow : artifactMetadataResult.get() )
2094             {
2095                 String artifactKey = artifactMetadataRow.getKey();
2096                 artifactMetadatas.add( mapArtifactMetadataStringColumnSlice( artifactKey, artifactMetadataRow.getColumnSlice() ) );
2097             }
2098         }
2099
2100         return mapArtifactFacetToArtifact( metadataFacetResult, artifactMetadatas );
2101     }
2102
2103     @Override
2104     public List<ArtifactMetadata> getArtifactsByProjectVersionAttribute( RepositorySession session, String key, String value, String repositoryId )
2105         throws MetadataRepositoryException
2106     {
2107         QueryResult<OrderedRows<String, String, String>> result =
2108             HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2109             .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
2110             .setColumnNames( PROJECT_ID.toString(), REPOSITORY_NAME.toString(), NAMESPACE_ID.toString(),
2111                              PROJECT_VERSION.toString() ) //
2112             .addEqualsExpression( key, value ) //
2113             .execute();
2114
2115         int count = result.get().getCount();
2116
2117         if ( count < 1 )
2118         {
2119             return Collections.emptyList();
2120         }
2121
2122         List<ArtifactMetadata> artifacts = new LinkedList<ArtifactMetadata>();
2123
2124         for ( Row<String, String, String> row : result.get() )
2125         {
2126             // TODO doing multiple queries, there should be a way to get all the artifactMetadatas for any number of
2127             // projects
2128             try
2129             {
2130                 artifacts.addAll( getArtifacts( session,
2131                     getStringValue( row.getColumnSlice(), REPOSITORY_NAME ),
2132                     getStringValue( row.getColumnSlice(), NAMESPACE_ID ),
2133                     getStringValue( row.getColumnSlice(), PROJECT_ID ), getStringValue( row.getColumnSlice(), PROJECT_VERSION ) ) );
2134             }
2135             catch ( MetadataResolutionException e )
2136             {
2137                 // never raised
2138                 throw new IllegalStateException( e );
2139             }
2140         }
2141         return artifacts;
2142     }
2143
2144     @Override
2145     public void removeArtifact( RepositorySession session, final String repositoryId, final String namespace, final String project,
2146                                 final String version, final String id )
2147         throws MetadataRepositoryException
2148     {
2149         logger.debug( "removeTimestampedArtifact repositoryId: '{}', namespace: '{}', project: '{}', version: '{}', id: '{}'",
2150                       repositoryId, namespace, project, version, id );
2151         String key =
2152             new ArtifactMetadataModel.KeyBuilder().withRepositoryId( repositoryId ).withNamespace( namespace ).withId(
2153                 id ).withProjectVersion( version ).withProject( project ).build();
2154
2155         this.artifactMetadataTemplate.deleteRow( key );
2156
2157         key = new ProjectVersionMetadataModel.KeyBuilder() //
2158             .withRepository( repositoryId ) //
2159             .withNamespace( namespace ) //
2160             .withProjectId( project ) //
2161             .withProjectVersion( version ) //
2162             .withId( id ) //
2163             .build();
2164
2165         this.projectVersionMetadataTemplate.deleteRow( key );
2166     }
2167
2168     @Override
2169     public void removeTimestampedArtifact( RepositorySession session, ArtifactMetadata artifactMetadata, String baseVersion )
2170         throws MetadataRepositoryException
2171     {
2172         logger.debug( "removeTimestampedArtifact repositoryId: '{}', namespace: '{}', project: '{}', version: '{}', id: '{}'",
2173                       artifactMetadata.getRepositoryId(), artifactMetadata.getNamespace(),
2174                       artifactMetadata.getProject(), baseVersion, artifactMetadata.getId() );
2175         String key =
2176             new ArtifactMetadataModel.KeyBuilder().withRepositoryId( artifactMetadata.getRepositoryId() ).withNamespace(
2177                 artifactMetadata.getNamespace() ).withId( artifactMetadata.getId() ).withProjectVersion(
2178                 baseVersion ).withProject( artifactMetadata.getProject() ).build();
2179
2180         this.artifactMetadataTemplate.deleteRow( key );
2181
2182     }
2183
2184     @Override
2185     public void removeFacetFromArtifact( RepositorySession session, final String repositoryId, final String namespace, final String project,
2186                                          final String version, final MetadataFacet metadataFacet )
2187         throws MetadataRepositoryException
2188     {
2189
2190         RangeSlicesQuery<String, String, String> query = HFactory //
2191             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2192             .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2193             .setColumnNames( NAMESPACE_ID.toString() ); //
2194
2195         query = query.addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
2196             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2197             .addEqualsExpression( PROJECT.toString(), project ) //
2198             .addEqualsExpression( VERSION.toString(), version );
2199
2200         QueryResult<OrderedRows<String, String, String>> result = query.execute();
2201
2202         for ( Row<String, String, String> row : result.get() )
2203         {
2204             this.artifactMetadataTemplate.deleteRow( row.getKey() );
2205         }
2206     }
2207
2208
2209     @Override
2210     public List<ArtifactMetadata> getArtifacts( RepositorySession session, final String repositoryId )
2211         throws MetadataRepositoryException
2212     {
2213
2214         RangeSlicesQuery<String, String, String> query = HFactory //
2215             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2216             .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2217             .setColumnNames( ArtifactMetadataModel.COLUMNS ); //
2218
2219         query = query.addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId );
2220
2221         QueryResult<OrderedRows<String, String, String>> result = query.execute();
2222
2223
2224
2225         List<ArtifactMetadata> artifactMetadatas = new ArrayList<>( result.get().getCount() );
2226
2227         for ( Row<String, String, String> row : result.get() )
2228         {
2229             String key = row.getKey();
2230             ColumnSlice<String, String> columnSlice = row.getColumnSlice();
2231             artifactMetadatas.add( mapArtifactMetadataStringColumnSlice( key, columnSlice ) );
2232
2233         }
2234
2235         return artifactMetadatas;
2236     }
2237
2238
2239     @Override
2240     public Collection<ProjectVersionReference> getProjectReferences( RepositorySession session, String repoId, String namespace, String projectId,
2241                                                                      String projectVersion )
2242         throws MetadataResolutionException
2243     {
2244         QueryResult<OrderedRows<String, String, String>> result = HFactory //
2245             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2246             .setColumnFamily( cassandraArchivaManager.getDependencyFamilyName() ) //
2247             .setColumnNames( "projectVersionMetadataModel.key" ) //
2248             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2249             .addEqualsExpression( GROUP_ID.toString(), namespace ) //
2250             .addEqualsExpression( ARTIFACT_ID.toString(), projectId ) //
2251             .addEqualsExpression( VERSION.toString(), projectVersion ) //
2252             .execute();
2253
2254         List<String> dependenciesIds = new ArrayList<>( result.get().getCount() );
2255
2256         for ( Row<String, String, String> row : result.get().getList() )
2257         {
2258             dependenciesIds.add( getStringValue( row.getColumnSlice(), "projectVersionMetadataModel.key" ) );
2259         }
2260
2261         List<ProjectVersionReference> references = new ArrayList<>( result.get().getCount() );
2262
2263         for ( String key : dependenciesIds )
2264         {
2265             ColumnFamilyResult<String, String> columnFamilyResult =
2266                 this.projectVersionMetadataTemplate.queryColumns( key );
2267             references.add( new ProjectVersionReference( ProjectVersionReference.ReferenceType.DEPENDENCY, //
2268                                                          columnFamilyResult.getString( PROJECT_ID.toString() ), //
2269                                                          columnFamilyResult.getString( NAMESPACE_ID.toString() ), //
2270                                                          columnFamilyResult.getString( PROJECT_VERSION.toString() ) ) );
2271         }
2272
2273         return references;
2274     }
2275
2276     @Override
2277     public void removeProjectVersion( RepositorySession session, final String repoId, final String namespace, final String projectId,
2278                                       final String projectVersion )
2279         throws MetadataRepositoryException
2280     {
2281
2282         QueryResult<OrderedRows<String, String, String>> result = HFactory //
2283             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2284             .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
2285             .setColumnNames( VERSION.toString() ) //
2286             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2287             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2288             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
2289             .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
2290             .execute();
2291
2292         for ( Row<String, String, String> row : result.get().getList() )
2293         {
2294             this.projectVersionMetadataTemplate.deleteRow( row.getKey() );
2295             removeMailingList( row.getKey() );
2296             removeLicenses( row.getKey() );
2297             removeDependencies( row.getKey() );
2298         }
2299
2300         RangeSlicesQuery<String, String, String> query = HFactory //
2301             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2302             .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2303             .setColumnNames( NAMESPACE_ID.toString() ); //
2304
2305         query = query.addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2306             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2307             .addEqualsExpression( PROJECT.toString(), projectId ) //
2308             .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion );
2309
2310         result = query.execute();
2311
2312         for ( Row<String, String, String> row : result.get() )
2313         {
2314             this.artifactMetadataTemplate.deleteRow( row.getKey() );
2315
2316         }
2317     }
2318
2319     @Override
2320     public Collection<ArtifactMetadata> getArtifacts( RepositorySession session, final String repoId, final String namespace,
2321                                                       final String projectId, final String projectVersion )
2322         throws MetadataResolutionException
2323     {
2324
2325         QueryResult<OrderedRows<String, String, String>> result =
2326             HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2327                 .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2328                 .setColumnNames( ArtifactMetadataModel.COLUMNS )//
2329                 .setRowCount( Integer.MAX_VALUE ) //
2330                 .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2331                 .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2332                 .addEqualsExpression( PROJECT.toString(), projectId ) //
2333                 .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
2334                 .execute();
2335
2336         if ( result.get() == null || result.get().getCount() < 1 )
2337         {
2338             return Collections.emptyList();
2339         }
2340
2341         List<ArtifactMetadata> artifactMetadatas = new ArrayList<>( result.get().getCount() );
2342
2343         for ( Row<String, String, String> row : result.get() )
2344         {
2345             String key = row.getKey();
2346             artifactMetadatas.add( mapArtifactMetadataStringColumnSlice( key, row.getColumnSlice() ) );
2347         }
2348
2349         result = HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2350             .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
2351             .setColumnNames( MetadataFacetModel.COLUMNS ) //
2352             .setRowCount( Integer.MAX_VALUE ) //
2353             .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2354             .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2355             .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
2356             .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
2357             .execute();
2358
2359         return mapArtifactFacetToArtifact(result, artifactMetadatas);
2360     }
2361
2362     /**
2363      * Attach metadata to each of the  ArtifactMetadata objects
2364      */
2365     private List<ArtifactMetadata> mapArtifactFacetToArtifact( QueryResult<OrderedRows<String, String, String>> result, List<ArtifactMetadata> artifactMetadatas) {
2366         if ( result.get() == null || result.get().getCount() < 1 )
2367         {
2368             return artifactMetadatas;
2369         }
2370
2371         final List<MetadataFacetModel> metadataFacetModels = new ArrayList<>( result.get().getCount() );
2372
2373         for ( Row<String, String, String> row : result.get() )
2374         {
2375             ColumnSlice<String, String> columnSlice = row.getColumnSlice();
2376             MetadataFacetModel metadataFacetModel = new MetadataFacetModel();
2377             metadataFacetModel.setFacetId( getStringValue( columnSlice, FACET_ID.toString() ) );
2378             metadataFacetModel.setName( getStringValue( columnSlice, NAME.toString() ) );
2379             metadataFacetModel.setValue( getStringValue( columnSlice, VALUE.toString() ) );
2380             metadataFacetModel.setKey( getStringValue( columnSlice, KEY.toString() ) );
2381             metadataFacetModel.setProjectVersion( getStringValue( columnSlice, PROJECT_VERSION.toString() ) );
2382             metadataFacetModels.add( metadataFacetModel );
2383         }
2384
2385         // rebuild MetadataFacet for artifacts
2386
2387         for ( final ArtifactMetadata artifactMetadata : artifactMetadatas )
2388         {
2389             Iterable<MetadataFacetModel> metadataFacetModelIterable =
2390                 Iterables.filter( metadataFacetModels, new Predicate<MetadataFacetModel>()
2391                 {
2392                     @Override
2393                     public boolean apply( MetadataFacetModel metadataFacetModel )
2394                     {
2395                         if ( metadataFacetModel != null )
2396                         {
2397                             return StringUtils.equals( artifactMetadata.getVersion(),
2398                                                        metadataFacetModel.getProjectVersion() );
2399                         }
2400                         return false;
2401                     }
2402                 } );
2403             Iterator<MetadataFacetModel> iterator = metadataFacetModelIterable.iterator();
2404             Map<String, List<MetadataFacetModel>> metadataFacetValuesPerFacetId = new HashMap<>();
2405             while ( iterator.hasNext() )
2406             {
2407                 MetadataFacetModel metadataFacetModel = iterator.next();
2408                 List<MetadataFacetModel> values = metadataFacetValuesPerFacetId.get( metadataFacetModel.getName() );
2409                 if ( values == null )
2410                 {
2411                     values = new ArrayList<>();
2412                     metadataFacetValuesPerFacetId.put( metadataFacetModel.getFacetId(), values );
2413                 }
2414                 values.add( metadataFacetModel );
2415
2416             }
2417
2418             for ( Map.Entry<String, List<MetadataFacetModel>> entry : metadataFacetValuesPerFacetId.entrySet() )
2419             {
2420                 MetadataFacetFactory metadataFacetFactory = getFacetFactory( entry.getKey() );
2421                 if ( metadataFacetFactory != null )
2422                 {
2423                     List<MetadataFacetModel> facetModels = entry.getValue();
2424                     if ( !facetModels.isEmpty() )
2425                     {
2426                         MetadataFacet metadataFacet = metadataFacetFactory.createMetadataFacet();
2427                         Map<String, String> props = new HashMap<>( facetModels.size() );
2428                         for ( MetadataFacetModel metadataFacetModel : facetModels )
2429                         {
2430                             props.put( metadataFacetModel.getKey(), metadataFacetModel.getValue() );
2431                         }
2432                         metadataFacet.fromProperties( props );
2433                         artifactMetadata.addFacet( metadataFacet );
2434                     }
2435                 }
2436             }
2437         }
2438
2439         return artifactMetadatas;
2440     }
2441
2442     @Override
2443     public void close()
2444         throws MetadataRepositoryException
2445     {
2446         logger.trace( "close" );
2447     }
2448
2449
2450     private static class ModelMapperHolder
2451     {
2452         private static ModelMapper MODEL_MAPPER = new ModelMapper();
2453     }
2454
2455     protected ModelMapper getModelMapper()
2456     {
2457         return ModelMapperHolder.MODEL_MAPPER;
2458     }
2459
2460     /**
2461      * This implementation just calls getArtifactsByAttribute( null, text, repositoryId ). We can't search artifacts by
2462      * any property.
2463      */
2464     @Override
2465     public List<ArtifactMetadata> searchArtifacts( final RepositorySession session, final String repositoryId,
2466                                                    final String text, final boolean exact )
2467         throws MetadataRepositoryException
2468     {
2469         return this.getArtifactsByAttribute( session, null, text, repositoryId );
2470     }
2471
2472     /**
2473      * The exact parameter is ignored as we can't do non exact searches in Cassandra
2474      */
2475     @Override
2476     public List<ArtifactMetadata> searchArtifacts( final RepositorySession session, final String repositoryId,
2477                                                    final String key, final String text, final boolean exact )
2478         throws MetadataRepositoryException
2479     {
2480         // TODO optimize
2481         List<ArtifactMetadata> artifacts = new LinkedList<ArtifactMetadata>();
2482         artifacts.addAll( this.getArtifactsByAttribute( session, key, text, repositoryId ) );
2483         artifacts.addAll( this.getArtifactsByProjectVersionAttribute( session, key, text, repositoryId ) );
2484         return artifacts;
2485     }
2486
2487     @Override
2488     public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repositoryId,
2489                                                        final QueryParameter queryParameter ) throws MetadataResolutionException
2490     {
2491         RangeSlicesQuery<String, String, String> query = HFactory //
2492             .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2493             .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName( ) ) //
2494             .setColumnNames( ArtifactMetadataModel.COLUMNS ); //
2495
2496         query = query.addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId );
2497
2498         QueryResult<OrderedRows<String, String, String>> result = query.execute();
2499
2500         try
2501         {
2502             return StreamSupport.stream( createResultSpliterator( result, ( Row<String, String, String> row, ArtifactMetadata last ) ->
2503                 mapArtifactMetadataStringColumnSlice( row.getKey( ), row.getColumnSlice( ) ) ), false )
2504                 .skip( queryParameter.getOffset( ) ).limit( queryParameter.getLimit( ) );
2505         }
2506         catch ( MetadataRepositoryException e )
2507         {
2508             throw new MetadataResolutionException( e.getMessage( ), e );
2509         }
2510     }
2511
2512     @Override
2513     public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repoId,
2514                                                        final String namespace, final String projectId, final String projectVersion,
2515                                                        final QueryParameter queryParameter ) throws MetadataResolutionException
2516     {
2517         // Currently we have to align the facets with the artifacts, which means querying artifacts, querying facets and combining them.
2518         // I so no stream friendly way to do this, so we just use the collection based method and return the stream.
2519         // TODO: Maybe we can query the facets for each artifact separately, but not sure, if this affects performance significantly
2520         //       We need some data to verify this.
2521         return getArtifacts( session, repoId, namespace, projectId, projectVersion ).stream( ).skip( queryParameter.getOffset( ) ).limit( queryParameter.getLimit( ) );
2522     }
2523 }