001package org.eclipse.aether.internal.impl;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.ByteArrayInputStream;
023import java.io.ByteArrayOutputStream;
024import java.io.Closeable;
025import java.io.File;
026import java.io.FileInputStream;
027import java.io.IOException;
028import java.io.RandomAccessFile;
029import java.util.Map;
030import java.util.Properties;
031
032import javax.inject.Named;
033import javax.inject.Singleton;
034
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038/**
039 * Manages access to a properties file.
040 */
041@Singleton
042@Named
043public final class DefaultTrackingFileManager
044    implements TrackingFileManager
045{
046    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultTrackingFileManager.class );
047
048    @Override
049    public Properties read( File file )
050    {
051        FileInputStream stream = null;
052        try
053        {
054            if ( !file.exists() )
055            {
056                return null;
057            }
058
059            stream = new FileInputStream( file );
060
061            Properties props = new Properties();
062            props.load( stream );
063
064            return props;
065        }
066        catch ( IOException e )
067        {
068            LOGGER.warn( "Failed to read tracking file {}", file, e );
069        }
070        finally
071        {
072            close( stream, file );
073        }
074
075        return null;
076    }
077
078    @Override
079    public Properties update( File file, Map<String, String> updates )
080    {
081        Properties props = new Properties();
082
083        File directory = file.getParentFile();
084        if ( !directory.mkdirs() && !directory.exists() )
085        {
086            LOGGER.warn( "Failed to create parent directories for tracking file {}", file );
087            return props;
088        }
089
090        RandomAccessFile raf = null;
091        try
092        {
093            raf = new RandomAccessFile( file, "rw" );
094
095            if ( file.canRead() )
096            {
097                byte[] buffer = new byte[(int) raf.length()];
098
099                raf.readFully( buffer );
100
101                ByteArrayInputStream stream = new ByteArrayInputStream( buffer );
102
103                props.load( stream );
104            }
105
106            for ( Map.Entry<String, String> update : updates.entrySet() )
107            {
108                if ( update.getValue() == null )
109                {
110                    props.remove( update.getKey() );
111                }
112                else
113                {
114                    props.setProperty( update.getKey(), update.getValue() );
115                }
116            }
117
118            ByteArrayOutputStream stream = new ByteArrayOutputStream( 1024 * 2 );
119
120            LOGGER.debug( "Writing tracking file {}", file );
121            props.store( stream, "NOTE: This is a Maven Resolver internal implementation file"
122                + ", its format can be changed without prior notice." );
123
124            raf.seek( 0 );
125            raf.write( stream.toByteArray() );
126            raf.setLength( raf.getFilePointer() );
127        }
128        catch ( IOException e )
129        {
130            LOGGER.warn( "Failed to write tracking file {}", file, e );
131        }
132        finally
133        {
134            close( raf, file );
135        }
136
137        return props;
138    }
139
140    private void close( Closeable closeable, File file )
141    {
142        if ( closeable != null )
143        {
144            try
145            {
146                closeable.close();
147            }
148            catch ( IOException e )
149            {
150                LOGGER.warn( "Error closing tracking file {}", file, e );
151            }
152        }
153    }
154
155}