/* * Copyright 2011 gitblit.com. * * Licensed 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. */ package com.gitblit.utils; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.Charset; /** * Common file utilities. * * @author James Moger * */ public class FileUtils { /** 1024 (number of bytes in one kilobyte) */ public static final int KB = 1024; /** 1024 {@link #KB} (number of bytes in one megabyte) */ public static final int MB = 1024 * KB; /** 1024 {@link #MB} (number of bytes in one gigabyte) */ public static final int GB = 1024 * MB; /** * Returns an int from a string representation of a file size. * e.g. 50m = 50 megabytes * * @param aString * @param defaultValue * @return an int value or the defaultValue if aString can not be parsed */ public static int convertSizeToInt(String aString, int defaultValue) { return (int) convertSizeToLong(aString, defaultValue); } /** * Returns a long from a string representation of a file size. * e.g. 50m = 50 megabytes * * @param aString * @param defaultValue * @return a long value or the defaultValue if aString can not be parsed */ public static long convertSizeToLong(String aString, long defaultValue) { // trim string and remove all spaces aString = aString.toLowerCase().trim(); StringBuilder sb = new StringBuilder(); for (String a : aString.split(" ")) { sb.append(a); } aString = sb.toString(); // identify value and unit int idx = 0; int len = aString.length(); while (Character.isDigit(aString.charAt(idx))) { idx++; if (idx == len) { break; } } long value = 0; String unit = null; try { value = Long.parseLong(aString.substring(0, idx)); unit = aString.substring(idx); } catch (Exception e) { return defaultValue; } if (unit.equals("g") || unit.equals("gb")) { return value * GB; } else if (unit.equals("m") || unit.equals("mb")) { return value * MB; } else if (unit.equals("k") || unit.equals("kb")) { return value * KB; } return defaultValue; } /** * Returns the byte [] content of the specified file. * * @param file * @return the byte content of the file */ public static byte [] readContent(File file) { byte [] buffer = new byte[(int) file.length()]; BufferedInputStream is = null; try { is = new BufferedInputStream(new FileInputStream(file)); is.read(buffer, 0, buffer.length); } catch (Throwable t) { System.err.println("Failed to read byte content of " + file.getAbsolutePath()); t.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException ioe) { System.err.println("Failed to close file " + file.getAbsolutePath()); ioe.printStackTrace(); } } } return buffer; } /** * Returns the string content of the specified file. * * @param file * @param lineEnding * @return the string content of the file */ public static String readContent(File file, String lineEnding) { StringBuilder sb = new StringBuilder(); InputStreamReader is = null; try { is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")); BufferedReader reader = new BufferedReader(is); String line = null; while ((line = reader.readLine()) != null) { sb.append(line); if (lineEnding != null) { sb.append(lineEnding); } } } catch (Throwable t) { System.err.println("Failed to read content of " + file.getAbsolutePath()); t.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException ioe) { System.err.println("Failed to close file " + file.getAbsolutePath()); ioe.printStackTrace(); } } } return sb.toString(); } /** * Writes the string content to the file. * * @param file * @param content */ public static void writeContent(File file, String content) { OutputStreamWriter os = null; try { os = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8")); BufferedWriter writer = new BufferedWriter(os); writer.append(content); writer.flush(); } catch (Throwable t) { System.err.println("Failed to write content of " + file.getAbsolutePath()); t.printStackTrace(); } finally { if (os != null) { try { os.close(); } catch (IOException ioe) { System.err.println("Failed to close file " + file.getAbsolutePath()); ioe.printStackTrace(); } } } } /** * Recursively traverses a folder and its subfolders to calculate the total * size in bytes. * * @param directory * @return folder size in bytes */ public static long folderSize(File directory) { if (directory == null || !directory.exists()) { return -1; } if (directory.isDirectory()) { long length = 0; for (File file : directory.listFiles()) { length += folderSize(file); } return length; } else if (directory.isFile()) { return directory.length(); } return 0; } /** * Delete a file or recursively delete a folder. * * @param fileOrFolder * @return true, if successful */ public static boolean delete(File fileOrFolder) { boolean success = false; if (fileOrFolder.isDirectory()) { File [] files = fileOrFolder.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { success |= delete(file); } else { success |= file.delete(); } } } } success |= fileOrFolder.delete(); return success; } /** * Copies a file or folder (recursively) to a destination folder. * * @param destinationFolder * @param filesOrFolders * @return * @throws FileNotFoundException * @throws IOException */ public static void copy(File destinationFolder, File... filesOrFolders) throws FileNotFoundException, IOException { destinationFolder.mkdirs(); for (File file : filesOrFolders) { if (file.isDirectory()) { copy(new File(destinationFolder, file.getName()), file.listFiles()); } else { File dFile = new File(destinationFolder, file.getName()); BufferedInputStream bufin = null; FileOutputStream fos = null; try { bufin = new BufferedInputStream(new FileInputStream(file)); fos = new FileOutputStream(dFile); int len = 8196; byte[] buff = new byte[len]; int n = 0; while ((n = bufin.read(buff, 0, len)) != -1) { fos.write(buff, 0, n); } } finally { try { if (bufin != null) bufin.close(); } catch (Throwable t) { } try { if (fos != null) fos.close(); } catch (Throwable t) { } } dFile.setLastModified(file.lastModified()); } } } /** * Determine the relative path between two files. Takes into account * canonical paths, if possible. * * @param basePath * @param path * @return a relative path from basePath to path */ public static String getRelativePath(File basePath, File path) { File exactBase = getExactFile(basePath); File exactPath = getExactFile(path); if (path.getAbsolutePath().startsWith(basePath.getAbsolutePath())) { // absolute base-path match return StringUtils.getRelativePath(basePath.getAbsolutePath(), path.getAbsolutePath()); } else if (exactPath.getPath().startsWith(exactBase.getPath())) { // canonical base-path match return StringUtils.getRelativePath(exactBase.getPath(), exactPath.getPath()); } else if (exactPath.getPath().startsWith(basePath.getAbsolutePath())) { // mixed path match return StringUtils.getRelativePath(basePath.getAbsolutePath(), exactPath.getPath()); } else if (path.getAbsolutePath().startsWith(exactBase.getPath())) { // mixed path match return StringUtils.getRelativePath(exactBase.getPath(), path.getAbsolutePath()); } // no relative relationship return null; } /** * Returns the exact path for a file. This path will be the canonical path * unless an exception is thrown in which case it will be the absolute path. * * @param path * @return the exact file */ public static File getExactFile(File path) { try { return path.getCanonicalFile(); } catch (IOException e) { return path.getAbsoluteFile(); } } public static File resolveParameter(String parameter, File aFolder, String path) { if (aFolder == null) { // strip any parameter reference path = path.replace(parameter, "").trim(); if (path.length() > 0 && path.charAt(0) == '/') { // strip leading / path = path.substring(1); } } else if (path.contains(parameter)) { // replace parameter with path path = path.replace(parameter, aFolder.getAbsolutePath()); } return new File(path); } } ' href='#n182'>182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
/*
 * Copyright 2011 gitblit.com.
 *
 * Licensed 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.
 */
package com.gitblit.models;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.gitblit.Constants.FederationPullStatus;
import com.gitblit.utils.StringUtils;

/**
 * Represents a federated server registration. Gitblit federation allows one
 * Gitblit instance to pull the repositories and configuration from another
 * Gitblit instance. This is a backup operation and can be considered something
 * like svn-sync.
 *
 */
public class FederationModel implements Serializable, Comparable<FederationModel> {

	private static final long serialVersionUID = 1L;

	public String name;

	public String url;

	public String token;

	public String frequency;

	public String folder;

	public boolean bare;

    public boolean mirror;

	public boolean mergeAccounts;

	public boolean sendStatus;

	public boolean notifyOnError;

	public List<String> exclusions = new ArrayList<String>();

	public List<String> inclusions = new ArrayList<String>();

	public Date lastPull;

	public Date nextPull;

	private Map<String, FederationPullStatus> results = new ConcurrentHashMap<String, FederationPullStatus>();

	/**
	 * The constructor for a remote server configuration.
	 *
	 * @param serverName
	 */
	public FederationModel(String serverName) {
		this.name = serverName;
		bare = true;
		mirror = true;
		this.lastPull = new Date(0);
		this.nextPull = new Date(0);
	}

	public boolean isIncluded(RepositoryModel repository) {
		// if exclusions has the all wildcard, then check for specific
		// inclusions
		if (exclusions.contains("*")) {
			for (String name : inclusions) {
				if (StringUtils.fuzzyMatch(repository.name, name)) {
					results.put(repository.name, FederationPullStatus.PENDING);
					return true;
				}
			}
			results.put(repository.name, FederationPullStatus.EXCLUDED);
			return false;
		}

		// named exclusions
		for (String name : exclusions) {
			if (StringUtils.fuzzyMatch(repository.name, name)) {
				results.put(repository.name, FederationPullStatus.EXCLUDED);
				return false;
			}
		}

		// included by default
		results.put(repository.name, FederationPullStatus.PENDING);
		return true;
	}

	/**
	 * Updates the pull status of a particular repository in this federation
	 * registration.
	 *
	 * @param repository
	 * @param status
	 */
	public void updateStatus(RepositoryModel repository, FederationPullStatus status) {
		if (!results.containsKey(repository.name)) {
			results.put(repository.name, FederationPullStatus.PENDING);
		}
		if (status != null) {
			results.put(repository.name, status);
		}
	}

	public List<RepositoryStatus> getStatusList() {
		List<RepositoryStatus> list = new ArrayList<RepositoryStatus>();
		for (Map.Entry<String, FederationPullStatus> entry : results.entrySet()) {
			list.add(new RepositoryStatus(entry.getKey(), entry.getValue()));
		}
		return list;
	}

	/**
	 * Iterates over the current pull results and returns the lowest pull
	 * status.
	 *
	 * @return the lowest pull status of the registration
	 */
	public FederationPullStatus getLowestStatus() {
		if (results.size() == 0) {
			return FederationPullStatus.PENDING;
		}
		FederationPullStatus status = FederationPullStatus.MIRRORED;
		for (FederationPullStatus result : results.values()) {
			if (result.ordinal() < status.ordinal()) {
				status = result;
			}
		}
		return status;
	}

	/**
	 * Returns true if this registration represents the result data sent by a
	 * pulling Gitblit instance.
	 *
	 * @return true, if this is result data
	 */
	public boolean isResultData() {
		return !url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://");
	}

	@Override
	public String toString() {
		return "Federated " + name + " (" + url + ")";
	}

	@Override
	public int compareTo(FederationModel o) {
		boolean r1 = isResultData();
		boolean r2 = o.isResultData();
		if ((r1 && r2) || (!r1 && !r2)) {
			// sort registrations and results by name
			return name.compareTo(o.name);
		}
		// sort registrations first
		if (r1) {
			return 1;
		}
		return -1;
	}

	/**
	 * Class that encapsulates a point-in-time pull result.
	 *
	 */
	public static class RepositoryStatus implements Serializable, Comparable<RepositoryStatus> {

		private static final long serialVersionUID = 1L;

		public final String name;
		public final FederationPullStatus status;

		RepositoryStatus(String name, FederationPullStatus status) {
			this.name = name;
			this.status = status;
		}

		@Override
		public int compareTo(RepositoryStatus o) {
			if (status.equals(o.status)) {
				return StringUtils.compareRepositoryNames(name, o.name);
			}
			return status.compareTo(o.status);
		}
	}
}