1 package org.apache.archiva.metadata.repository.cassandra;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
22 import com.datastax.oss.driver.api.core.CqlSession;
23 import com.datastax.oss.driver.api.core.CqlSessionBuilder;
24 import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
25 import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
26 import com.datastax.oss.driver.api.core.type.DataTypes;
27 import com.datastax.oss.driver.api.querybuilder.schema.CreateIndex;
28 import com.datastax.oss.driver.api.querybuilder.schema.CreateKeyspace;
29 import com.datastax.oss.driver.api.querybuilder.schema.CreateTableWithOptions;
30 import org.apache.archiva.metadata.repository.RepositorySessionFactoryBean;
31 import org.apache.commons.lang3.StringUtils;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34 import org.springframework.beans.factory.annotation.Value;
35 import org.springframework.context.ApplicationContext;
36 import org.springframework.stereotype.Service;
38 import javax.annotation.PostConstruct;
39 import javax.annotation.PreDestroy;
40 import javax.inject.Inject;
41 import java.time.Duration;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.List;
46 import static com.datastax.oss.driver.api.querybuilder.SchemaBuilder.*;
47 import static org.apache.archiva.metadata.repository.cassandra.model.ColumnNames.*;
50 * FIXME make all configuration not hardcoded :-)
52 * @author Olivier Lamy
55 @Service( "archivaEntityManagerFactory#cassandra" )
56 public class DefaultCassandraArchivaManager
57 implements CassandraArchivaManager
60 private static final Logger logger = LoggerFactory.getLogger( DefaultCassandraArchivaManager.class );
63 private ApplicationContext applicationContext;
65 private static final String CLUSTER_NAME = "archiva";
67 private static final String KEYSPACE_NAME = "ArchivaKeySpace";
69 private boolean started;
72 private String repositoryFamilyName = "repository";
74 private String namespaceFamilyName = "namespace";
76 private String projectFamilyName = PROJECT.toString( );
78 private String projectVersionMetadataFamilyName = "projectversionmetadata";
80 private String artifactMetadataFamilyName = "artifactmetadata";
82 private String metadataFacetFamilyName = "metadatafacet";
84 private String mailingListFamilyName = "mailinglist";
86 private String licenseFamilyName = "license";
88 private String dependencyFamilyName = "dependency";
90 private String checksumFamilyName = "checksum";
93 private static String[] projectVersionMetadataColumns;
98 projectVersionMetadataColumns = new String[]{
100 NAMESPACE_ID.toString( ),
101 REPOSITORY_NAME.toString( ),
102 PROJECT_VERSION.toString( ),
103 PROJECT_ID.toString( ),
104 DESCRIPTION.toString( ),
108 VERSION_PROPERTIES.toString( ),
110 "ciManagement.system",
112 "issueManagement.system",
113 "issueManagement.url",
118 "scm.developerConnection"
120 Arrays.sort( projectVersionMetadataColumns );
123 @Value( "${cassandra.host}" )
124 private String cassandraHost;
126 @Value( "${cassandra.port}" )
127 private String cassandraPort;
129 @Value( "${cassandra.maxActive}" )
130 private int maxActive;
132 @Value( "${cassandra.driverTimeoutMs}" )
133 private int driverTimeoutMs;
135 @Value( "${cassandra.readConsistencyLevel}" )
136 private String readConsistencyLevel;
138 @Value( "${cassandra.writeConsistencyLevel}" )
139 private String writeConsistencyLevel;
141 @Value( "${cassandra.replicationFactor}" )
142 private int replicationFactor;
144 @Value( "${cassandra.keyspace.name}" )
145 private String keyspaceName;
147 @Value( "${cassandra.cluster.name}" )
148 private String clusterName;
151 private RepositorySessionFactoryBean repositorySessionFactoryBean;
153 DriverConfigLoader configLoader;
155 CqlSession cqlSession;
158 public CqlSessionBuilder getSessionBuilder( )
160 return CqlSession.builder( ).withConfigLoader( configLoader ).withKeyspace( keyspaceName ).withLocalDatacenter( "datacenter1" );
164 public CqlSession getSession( )
166 if (cqlSession==null || cqlSession.isClosed()) {
167 this.cqlSession = getSessionBuilder( ).build( );
169 return this.cqlSession;
173 public void initialize( )
175 // skip initialisation if not cassandra
176 if ( !StringUtils.equals( repositorySessionFactoryBean.getId( ), "cassandra" ) )
181 List<String> hostNames = new ArrayList<>( );
182 hostNames.add( cassandraHost + ":" + cassandraPort );
183 System.out.println( "Contact point: " + cassandraHost + ":" + cassandraPort );
185 DriverConfigLoader.programmaticBuilder( )
187 .withStringList( DefaultDriverOption.CONTACT_POINTS, hostNames )
188 .withInt( DefaultDriverOption.CONNECTION_POOL_LOCAL_SIZE, maxActive )
189 .withInt( DefaultDriverOption.CONNECTION_POOL_REMOTE_SIZE, maxActive )
190 //.withInt( DefaultDriverOption.CONNECTION_MAX_REQUESTS, maxActive )
191 .withString( DefaultDriverOption.REQUEST_CONSISTENCY, readConsistencyLevel )
192 .withDuration( DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofMillis( driverTimeoutMs ) )
197 CreateKeyspace cKeySpace = createKeyspace( keyspaceName ).ifNotExists( ).withSimpleStrategy( replicationFactor );
198 CqlSession.builder( ).withConfigLoader( configLoader ).withLocalDatacenter( "datacenter1" ).build().execute( cKeySpace.build( ) );
201 CqlSession session = getSession( );
207 String tableName = getNamespaceFamilyName( );
208 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
209 .withPartitionKey( CassandraArchivaManager.DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
210 .withColumn( NAME.toString( ), DataTypes.TEXT )
211 .withColumn( REPOSITORY_NAME.toString( ), DataTypes.TEXT )
212 .withCompactStorage( );
213 session.execute( table.build( ) );
214 CreateIndex index = createIndex( NAME.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( NAME.toString( ) );
215 session.execute( index.build( ) );
216 index = createIndex( REPOSITORY_NAME.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( REPOSITORY_NAME.toString( ) );
217 session.execute( index.build( ) );
222 String tableName = getRepositoryFamilyName( );
223 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
224 .withPartitionKey( CassandraArchivaManager.DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
225 .withColumn( REPOSITORY_NAME.toString( ), DataTypes.TEXT )
226 .withCompactStorage( );
227 session.execute( table.build( ) );
228 CreateIndex index = createIndex( REPOSITORY_NAME.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( REPOSITORY_NAME.toString( ) );
229 session.execute( index.build( ) );
235 String tableName = getProjectFamilyName( );
236 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
237 .withPartitionKey( CassandraArchivaManager.DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
238 .withColumn( PROJECT_ID.toString( ), DataTypes.TEXT )
239 .withColumn( REPOSITORY_NAME.toString( ), DataTypes.TEXT )
240 .withColumn( NAMESPACE_ID.toString( ), DataTypes.TEXT )
241 .withColumn( PROJECT_PROPERTIES.toString( ), DataTypes.frozenMapOf( DataTypes.TEXT, DataTypes.TEXT ) )
242 .withCompactStorage( );
243 session.execute( table.build( ) );
244 CreateIndex index = createIndex( PROJECT_ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( PROJECT_ID.toString( ) );
245 session.execute( index.build( ) );
246 index = createIndex( REPOSITORY_NAME.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( REPOSITORY_NAME.toString( ) );
247 session.execute( index.build( ) );
248 index = createIndex( NAMESPACE_ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( NAMESPACE_ID.toString( ) );
249 session.execute( index.build( ) );
253 // Project Version Metadata Model
255 String tableName = getProjectVersionMetadataFamilyName( );
256 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
257 .withPartitionKey( CassandraArchivaManager.DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
258 .withColumn( NAMESPACE_ID.toString( ), DataTypes.TEXT )
259 .withColumn( REPOSITORY_NAME.toString( ), DataTypes.TEXT )
260 .withColumn( PROJECT_VERSION.toString( ), DataTypes.TEXT )
261 .withColumn( PROJECT_ID.toString( ), DataTypes.TEXT )
262 .withColumn( DESCRIPTION.toString( ), DataTypes.TEXT )
263 .withColumn( URL.toString( ), DataTypes.TEXT )
264 .withColumn( NAME.toString(), DataTypes.TEXT )
265 .withColumn( VERSION.toString(), DataTypes.TEXT )
266 .withColumn( VERSION_PROPERTIES.toString(), DataTypes.mapOf( DataTypes.TEXT, DataTypes.TEXT ) )
267 .withColumn( "incomplete", DataTypes.BOOLEAN )
268 .withColumn( "\"ciManagement.system\"", DataTypes.TEXT )
269 .withColumn( "\"ciManagement.url\"", DataTypes.TEXT )
270 .withColumn( "\"issueManagement.system\"", DataTypes.TEXT )
271 .withColumn( "\"issueManagement.url\"", DataTypes.TEXT )
272 .withColumn( "\"organization.name\"", DataTypes.TEXT )
273 .withColumn( "\"organization.url\"", DataTypes.TEXT )
274 .withColumn( "\"scm.url\"", DataTypes.TEXT )
275 .withColumn( "\"scm.connection\"", DataTypes.TEXT )
276 .withColumn( "\"scm.developerConnection\"", DataTypes.TEXT );
277 session.execute( table.build( ) );
278 CreateIndex index = createIndex( NAMESPACE_ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( NAMESPACE_ID.toString( ) );
279 session.execute( index.build( ) );
280 index = createIndex( REPOSITORY_NAME.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( REPOSITORY_NAME.toString( ) );
281 session.execute( index.build( ) );
282 index = createIndex( PROJECT_VERSION.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( PROJECT_VERSION.toString( ) );
283 session.execute( index.build( ) );
284 index = createIndex( PROJECT_ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( PROJECT_ID.toString( ) );
285 session.execute( index.build( ) );
286 index = createIndex( VERSION_PROPERTIES.toString( ) + "_idx" ).ifNotExists( ).onTable( tableName ).andColumnEntries( VERSION_PROPERTIES.toString( ) );
287 session.execute( index.build( ) );
290 // Artifact Metadata Model
292 String tableName = getArtifactMetadataFamilyName( );
293 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
294 .withPartitionKey( CassandraArchivaManager.DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
295 .withColumn( ID.toString( ), DataTypes.TEXT )
296 .withColumn( REPOSITORY_NAME.toString( ), DataTypes.TEXT )
297 .withColumn( NAMESPACE_ID.toString( ), DataTypes.TEXT )
298 .withColumn( PROJECT_ID.toString( ), DataTypes.TEXT )
299 .withColumn( PROJECT_VERSION.toString( ), DataTypes.TEXT )
300 .withColumn( VERSION.toString( ), DataTypes.TEXT )
301 .withColumn( WHEN_GATHERED.toString( ), DataTypes.BIGINT )
302 .withColumn( SHA1.toString( ), DataTypes.TEXT )
303 .withColumn( MD5.toString( ), DataTypes.TEXT )
304 .withColumn( FILE_LAST_MODIFIED.toString(), DataTypes.BIGINT)
305 .withColumn( SIZE.toString(), DataTypes.BIGINT )
306 .withCompactStorage( );
307 session.execute( table.build( ) );
309 CreateIndex index = createIndex( ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( ID.toString( ) );
310 session.execute( index.build( ) );
311 index = createIndex( REPOSITORY_NAME.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( REPOSITORY_NAME.toString( ) );
312 session.execute( index.build( ) );
313 index = createIndex( NAMESPACE_ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( NAMESPACE_ID.toString( ) );
314 session.execute( index.build( ) );
315 index = createIndex( PROJECT_ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( PROJECT_ID.toString( ) );
316 session.execute( index.build( ) );
317 index = createIndex( PROJECT_VERSION.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( PROJECT_VERSION.toString( ) );
318 session.execute( index.build( ) );
319 index = createIndex( VERSION.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( VERSION.toString( ) );
320 session.execute( index.build( ) );
321 index = createIndex( WHEN_GATHERED.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( WHEN_GATHERED.toString( ) );
322 session.execute( index.build( ) );
323 index = createIndex( SHA1.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( SHA1.toString( ) );
324 session.execute( index.build( ) );
325 index = createIndex( MD5.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( MD5.toString( ) );
326 session.execute( index.build( ) );
329 // Metadata Facet Model
331 String tableName = getMetadataFacetFamilyName( );
332 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
333 .withPartitionKey( CassandraArchivaManager.DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
334 .withColumn( FACET_ID.toString( ), DataTypes.TEXT )
335 .withColumn( REPOSITORY_NAME.toString( ), DataTypes.TEXT )
336 .withColumn( NAME.toString( ), DataTypes.TEXT )
337 .withColumn( NAMESPACE_ID.toString( ), DataTypes.TEXT )
338 .withColumn( PROJECT_ID.toString( ), DataTypes.TEXT )
339 .withColumn( PROJECT_VERSION.toString( ), DataTypes.TEXT )
340 .withColumn( KEY.toString(), DataTypes.TEXT )
341 .withColumn( VALUE.toString(), DataTypes.TEXT)
342 .withColumn( WHEN_GATHERED.toString(), DataTypes.BIGINT )
343 .withCompactStorage( );
344 session.execute( table.build( ) );
346 CreateIndex index = createIndex( FACET_ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( FACET_ID.toString( ) );
347 session.execute( index.build( ) );
348 index = createIndex( REPOSITORY_NAME.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( REPOSITORY_NAME.toString( ) );
349 session.execute( index.build( ) );
350 index = createIndex( NAME.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( NAME.toString( ) );
351 session.execute( index.build( ) );
352 index = createIndex( NAMESPACE_ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( NAMESPACE_ID.toString( ) );
353 session.execute( index.build( ) );
354 index = createIndex( PROJECT_ID.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( PROJECT_ID.toString( ) );
355 session.execute( index.build( ) );
356 index = createIndex( PROJECT_VERSION.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( PROJECT_VERSION.toString( ) );
357 session.execute( index.build( ) );
361 String tableName = getChecksumFamilyName( );
362 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
363 .withPartitionKey( DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
364 .withColumn( "\"artifactMetadataModel.key\"", DataTypes.TEXT )
365 .withColumn( CHECKSUM_ALG.toString( ), DataTypes.TEXT )
366 .withColumn( CHECKSUM_VALUE.toString( ), DataTypes.TEXT )
367 .withColumn( REPOSITORY_NAME.toString( ), DataTypes.TEXT )
368 .withCompactStorage( );
369 session.execute( table.build( ) );
371 CreateIndex index = createIndex( CHECKSUM_ALG.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( CHECKSUM_ALG.toString( ) );
372 session.execute( index.build( ) );
373 index = createIndex( CHECKSUM_VALUE.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( CHECKSUM_VALUE.toString( ) );
374 session.execute( index.build( ) );
375 index = createIndex( REPOSITORY_NAME.toString( ) ).ifNotExists( ).onTable( tableName ).andColumn( REPOSITORY_NAME.toString( ) );
376 session.execute( index.build( ) );
380 String tableName = getMailingListFamilyName( );
381 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
382 .withPartitionKey( CassandraArchivaManager.DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
383 .withColumn( NAME.toString(), DataTypes.TEXT )
384 .withColumn( "\"projectVersionMetadataModel.key\"", DataTypes.TEXT )
385 .withColumn( "mainArchiveUrl", DataTypes.TEXT )
386 .withColumn( "postAddress", DataTypes.TEXT )
387 .withColumn( "subscribeAddress", DataTypes.TEXT )
388 .withColumn( "unsubscribeAddress", DataTypes.TEXT )
389 .withColumn( "otherArchive", DataTypes.frozenListOf( DataTypes.TEXT ) )
390 .withCompactStorage( );
391 session.execute( table.build( ) );
393 CreateIndex index = createIndex( "\"projectVersionMetadataModel_key\"" ).ifNotExists( ).onTable( tableName ).andColumn( "\"\"projectVersionMetadataModel.key\"\"" );
394 session.execute( index.build( ) );
399 String tableName = getLicenseFamilyName( );
400 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
401 .withPartitionKey( CassandraArchivaManager.DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
402 .withColumn( "\"projectVersionMetadataModel.key\"", DataTypes.TEXT )
403 .withColumn( NAME.toString(), DataTypes.TEXT )
404 .withColumn( URL.toString(), DataTypes.TEXT )
405 .withCompactStorage( );
406 session.execute( table.build( ) );
408 CreateIndex index = createIndex( "\"projectVersionMetadataModel_key\"" ).ifNotExists( ).onTable( tableName ).andColumn( "\"\"projectVersionMetadataModel.key\"\"" );
409 session.execute( index.build( ) );
414 String tableName = getDependencyFamilyName( );
415 CreateTableWithOptions table = createTable( keyspaceName, tableName ).ifNotExists( )
416 .withPartitionKey( CassandraArchivaManager.DEFAULT_PRIMARY_KEY, DataTypes.TEXT )
417 .withColumn( REPOSITORY_NAME.toString( ), DataTypes.TEXT )
418 .withColumn( GROUP_ID.toString( ), DataTypes.TEXT )
419 .withColumn( ARTIFACT_ID.toString( ), DataTypes.TEXT )
420 .withColumn( VERSION.toString( ), DataTypes.TEXT )
421 .withColumn( "\"projectVersionMetadataModel.key\"", DataTypes.TEXT )
422 .withColumn( "classifier", DataTypes.TEXT )
423 .withColumn( "optional", DataTypes.TEXT )
424 .withColumn( "scope", DataTypes.TEXT )
425 .withColumn( "systemPath", DataTypes.TEXT )
426 .withColumn( "type", DataTypes.TEXT )
427 .withCompactStorage( );
429 session.execute( table.build( ) );
431 CreateIndex index = createIndex( "groupIdIdx" ).ifNotExists( ).onTable( tableName ).andColumn( GROUP_ID.toString( ) );
432 session.execute( index.build( ) );
433 index = createIndex( "\"projectVersionMetadataModel_key\"" ).ifNotExists( ).onTable( tableName ).andColumn( "\"\"projectVersionMetadataModel.key\"\"" );
434 session.execute( index.build( ) );
450 public void shutdown( )
452 if (this.cqlSession!=null) {
453 this.cqlSession.close( );
459 public boolean started( )
466 public String getRepositoryFamilyName( )
468 return repositoryFamilyName;
472 public String getNamespaceFamilyName( )
474 return namespaceFamilyName;
478 public String getProjectFamilyName( )
480 return projectFamilyName;
484 public String getProjectVersionMetadataFamilyName( )
486 return projectVersionMetadataFamilyName;
489 public String[] getProjectVersionMetadataColumns() {
490 return projectVersionMetadataColumns;
494 public String getArtifactMetadataFamilyName( )
496 return artifactMetadataFamilyName;
500 public String getMetadataFacetFamilyName( )
502 return metadataFacetFamilyName;
506 public String getMailingListFamilyName( )
508 return mailingListFamilyName;
512 public String getLicenseFamilyName( )
514 return licenseFamilyName;
518 public String getDependencyFamilyName( )
520 return dependencyFamilyName;
524 public String getChecksumFamilyName( )
526 return checksumFamilyName;
530 public DriverConfigLoader getConfigLoader( )
536 public String getKeyspaceName( )