001/*
002 *  Copyright 2013 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.skincommons;
017
018import java.io.IOException;
019import java.nio.charset.StandardCharsets;
020import java.nio.file.Files;
021import java.nio.file.Path;
022import java.time.ZonedDateTime;
023import java.time.format.DateTimeFormatter;
024import java.util.Date;
025import java.util.List;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.logger.AbstractLogEnabled;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.avalon.framework.service.Serviceable;
032import org.apache.avalon.framework.thread.ThreadSafe;
033
034import org.ametys.core.user.CurrentUserProvider;
035import org.ametys.core.user.UserIdentity;
036import org.ametys.core.util.DateUtils;
037import org.ametys.core.util.path.PathUtils;
038
039/**
040 * Component to manage lock on skin directories.
041 *
042 */
043public class SkinLockManager extends AbstractLogEnabled implements Component, ThreadSafe, Serviceable
044{
045    /** The Avalon role name */
046    public static final String ROLE = SkinLockManager.class.getName();
047    
048    private CurrentUserProvider _userProvider;
049    
050    @Override
051    public void service(ServiceManager smanager) throws ServiceException
052    {
053        _userProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
054    }
055    
056    /**
057     * Determines if the current user can write in skin directory
058     * @param file The skin directory
059     * @return <code>true</code> if the skin directory is not locked by another user
060     * @throws IOException If an error occurred
061     */
062    public boolean canWrite (Path file) throws IOException
063    {
064        return getLockOwner(file).equals(_userProvider.getUser());
065    }
066    
067    /**
068     * Create or update .lock file
069     * @param file The skin directory
070     * @param toolId The id of tool responsible for lock.
071     * @throws IOException If an error occurred
072     */
073    public void updateLockFile (Path file, String toolId) throws IOException
074    {
075        Path lockFile = file.resolve(".lock");
076        if (Files.exists(lockFile))
077        {
078            Files.delete(lockFile);
079        }
080        
081        String content = UserIdentity.userIdentityToString(_userProvider.getUser()) + "\n"
082                + DateUtils.getISODateTimeFormatter().format(ZonedDateTime.now()) + "\n"
083                + toolId;
084        Files.writeString(lockFile, content, StandardCharsets.UTF_8); 
085    }
086    
087    /**
088     * Remove lock file
089     * @param file The skin directory
090     */
091    public void unlock (Path file)
092    {
093        Path lockFile = file.resolve(".lock");
094        if (Files.exists(lockFile))
095        {
096            PathUtils.deleteQuietly(lockFile);
097        }
098    }
099    
100    /**
101     * Determines if the skin directory is locked
102     * @param file The skin directory
103     * @return <code>true</code> if the skin directory is locked
104     */
105    public boolean isLocked (Path file)
106    {
107        Path lockFile = file.resolve(".lock");
108        return Files.exists(lockFile);
109    }
110    
111    /**
112     * Get lock owner
113     * @param file The skin directory
114     * @return The lock owner or null.
115     * @throws IOException if an error occurs while manipulating files
116     */
117    public UserIdentity getLockOwner(Path file) throws IOException
118    {
119        Path lockFile = file.resolve(".lock");
120        if (!Files.exists(lockFile))
121        {
122            return null;
123        }
124        
125        List<String> lines = Files.readAllLines(lockFile, StandardCharsets.UTF_8);
126        if (!lines.isEmpty())
127        {
128            return UserIdentity.stringToUserIdentity(lines.get(0));
129        }
130        
131        return null;
132    }
133    
134    /**
135     * Get tool responsible for lock
136     * @param file The skin directory
137     * @return The tool responsible for lock or <code>null</code>
138     * @throws IOException if an error occurs while manipulating files
139     */
140    public String getLockTool(Path file)  throws IOException
141    {
142        Path lockFile = file.resolve(".lock");
143        if (!Files.exists(lockFile))
144        {
145            return null;
146        }
147        
148        List<String> lines = Files.readAllLines(lockFile, StandardCharsets.UTF_8);
149        if (!lines.isEmpty() && lines.size() > 2)
150        {
151            return lines.get(2);
152        }
153            
154        return null;
155    }
156    
157    /**
158     * Get the last modified date
159     * @param file The skin directory
160     * @return the last modified date
161     * @throws IOException if an error occurs while manipulating files
162     */
163    public Date lastModified(Path file) throws IOException
164    {
165        Path lockFile = file.resolve(".lock");
166        if (!Files.exists(lockFile))
167        {
168            return null;
169        }
170        
171        List<String> lines = Files.readAllLines(lockFile, StandardCharsets.UTF_8);
172        if (!lines.isEmpty() && lines.size() > 1)
173        {
174            return Date.from(ZonedDateTime.parse(lines.get(1), DateTimeFormatter.ISO_DATE_TIME).toInstant());
175        }
176
177        return null;
178    }
179}