blob: dd915102b2fa743b740e0901ce1f1904ab070197 [file] [log] [blame]
package org.apache.maven.index.reader;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.maven.index.reader.WritableResourceHandler.WritableResource;
import java.io.Closeable;
import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import static org.apache.maven.index.reader.Utils.loadProperties;
import static org.apache.maven.index.reader.Utils.storeProperties;
/**
* Maven 2 Index writer that writes chunk and maintains published property file.
* <p/>
* <strong>Currently no incremental update is supported, as the deleteion states should be maintained by
* caller</strong>. Hence, this writer will always produce the "main" chunk only.
*
* @since 5.1.2
*/
public class IndexWriter
implements Closeable
{
private static final int INDEX_V1 = 1;
private final WritableResourceHandler local;
private final Properties localIndexProperties;
private final boolean incremental;
private final String nextChunkCounter;
private final String nextChunkName;
public IndexWriter( final WritableResourceHandler local, final String indexId, final boolean incrementalSupported )
throws IOException
{
if ( local == null )
{
throw new NullPointerException( "local resource handler null" );
}
if ( indexId == null )
{
throw new NullPointerException( "indexId null" );
}
this.local = local;
Properties indexProperties = loadProperties( local.locate( Utils.INDEX_FILE_PREFIX + ".properties" ) );
if ( incrementalSupported && indexProperties != null )
{
this.localIndexProperties = indexProperties;
// existing index, this is incremental publish, and we will add new chunk
String localIndexId = localIndexProperties.getProperty( "nexus.index.id" );
if ( localIndexId == null || !localIndexId.equals( indexId ) )
{
throw new IllegalArgumentException(
"index already exists and indexId mismatch or unreadable: " + localIndexId + ", " + indexId );
}
this.incremental = true;
this.nextChunkCounter = calculateNextChunkCounter();
this.nextChunkName = Utils.INDEX_FILE_PREFIX + "." + nextChunkCounter + ".gz";
}
else
{
// non-existing index, create published index from scratch
this.localIndexProperties = new Properties();
this.localIndexProperties.setProperty( "nexus.index.id", indexId );
this.localIndexProperties.setProperty( "nexus.index.chain-id", UUID.randomUUID().toString() );
this.incremental = false;
this.nextChunkCounter = null;
this.nextChunkName = Utils.INDEX_FILE_PREFIX + ".gz";
}
}
/**
* Returns the index context ID that published index has set.
*/
public String getIndexId()
{
return localIndexProperties.getProperty( "nexus.index.id" );
}
/**
* Returns the {@link Date} when index was last published or {@code null} if this is first publishing. In other
* words,returns {@code null} when {@link #isIncremental()} returns {@code false}. After this writer is closed, the
* return value is updated to "now" (in {@link #close() method}.
*/
public Date getPublishedTimestamp()
{
try
{
String timestamp = localIndexProperties.getProperty( "nexus.index.timestamp" );
if ( timestamp != null )
{
return Utils.INDEX_DATE_FORMAT.parse( timestamp );
}
return null;
}
catch ( ParseException e )
{
throw new RuntimeException( "Corrupt date", e );
}
}
/**
* Returns {@code true} if incremental publish is about to happen.
*/
public boolean isIncremental()
{
return incremental;
}
/**
* Returns the chain id of published index. If {@link #isIncremental()} is {@code false}, this is the newly
* generated chain ID.
*/
public String getChainId()
{
return localIndexProperties.getProperty( "nexus.index.chain-id" );
}
/**
* Returns the next chunk name about to be published.
*/
public String getNextChunkName()
{
return nextChunkName;
}
/**
* Writes out the record iterator and returns the written record count.
*/
public int writeChunk( final Iterator<Map<String, String>> iterator )
throws IOException
{
int written;
try ( WritableResource writableResource = local.locate( nextChunkName ) )
{
final ChunkWriter chunkWriter =
new ChunkWriter( nextChunkName, writableResource.write(), INDEX_V1, new Date() );
try
{
written = chunkWriter.writeChunk( iterator );
}
finally
{
chunkWriter.close();
}
if ( incremental )
{
// TODO: update main gz file
}
return written;
}
}
/**
* Closes the underlying {@link ResourceHandler} and synchronizes published index properties, so remote clients
* becomes able to consume newly published index. If sync is not desired (ie. due to aborted publish), then this
* method should NOT be invoked, but rather the {@link ResourceHandler} that caller provided in constructor of
* this class should be closed manually.
*/
public void close()
throws IOException
{
try
{
if ( incremental )
{
localIndexProperties.setProperty( "nexus.index.last-incremental", nextChunkCounter );
}
localIndexProperties.setProperty( "nexus.index.timestamp", Utils.INDEX_DATE_FORMAT.format( new Date() ) );
storeProperties( local.locate( Utils.INDEX_FILE_PREFIX + ".properties" ), localIndexProperties );
}
finally
{
local.close();
}
}
/**
* Calculates the chunk names that needs to be fetched.
*/
private String calculateNextChunkCounter()
{
String lastChunkCounter = localIndexProperties.getProperty( "nexus.index.last-incremental" );
if ( lastChunkCounter != null )
{
return String.valueOf( Integer.parseInt( lastChunkCounter ) + 1 );
}
else
{
return "1";
}
}
}