Статьи

Встраивание JGit: первый взгляд

Пару лет назад мне понадобился паук, чтобы провести раскопки данных для аналитического проекта. Я нашел проект под названием JSpider, который выглядел великолепно, и эй, кредо с открытым исходным кодом — «используй то, что есть, не воссоздай колесо». Ну, это не так хорошо: дело было полным хлопот. Я скачал исходник и, к моему ужасу и ужасу, обнаружил, что это было до Java 5. Поэтому я написал свой собственный паук. С тех пор у меня было много поводов подумать, что делает паук? Он должен извлекать ссылки со страниц, чтобы продолжать работать, поэтому у него есть свои потребности, но что он делает? Недавно я работал над скребком, зная, что я закончу его, когда закончу. Потому что я пришел к выводу, что в целом, пауки должны сосредоточиться на части проблемы обнаружения:Следующие потоки и раскопки лежащей в основе топологической логики, и работа по получению информации со страницы должна выполняться в другом месте, либо скребками (если у нас есть конкретные элементы, которые мы хотим удалить из запутанных веб-катакомб), либо простыми индексаторами, если мы собираюсь просто выставить наши выводы для поиска. Я, вероятно, позже напишу еще немного об архитектуре паутинга, это интересный случай, когда я могу разработать четкое разграничение обязанностей, которое можно расширить.Интересный случай, когда можно разработать четкое разграничение обязанностей, которое можно расширить.Интересный случай, когда можно разработать четкое разграничение обязанностей, которое можно расширить.

Между тем, однажды, когда я размышлял о моем пауке некоторое время назад, меня осенило, что, возможно, недостающий кусочек в ландшафте паука — версионирование. Концептуально говоря, интересный вопрос заключается в том, как паук может сказать, что может что-то открыть, если не знает, что видел раньше? На самом деле, при реализации пауков, видимый список является обязательным. Это своего рода переход к следующему диахроническому измерению: на самом деле нет никакой причины переиндексировать или перекодировать страницу, если на ней ничего не изменилось.

Сначала я думал о реализации чего-то вроде контрольной суммы, но потом подумал, что это довольно глупо. Я также думал, что может быть реальная ценность в поддержании различий и истории для всех страниц на сайте.

Итак, я пошел и получил JGit.

Часть приобретения была довольно гладкой, хотя на странице загрузок создается впечатление, что у них есть хранилище maven, хотя на самом деле их нет. (Это был хороший способ начать мое путешествие по усыновлению с открытым исходным кодом: у него было чувство Ворда Дем Гесетца: вы знаете, что вещь существует, и она есть, но вас отправляют к двери, которой не существует. Затем вмешался мой верный друг Нексус, и оказалось, что источник jgit находится в публичном репозитории jboss. Номера версий были разными, длинными и некрасивыми, но, эй, включение зависимостей прошло довольно быстро, и я смог импортировать классы.

Как часто бывает с подобными библиотеками, существует низкоуровневая версия API, а затем более высокая версия (называемая фарфором). Я хотел написать модульный тест, который показал бы, что я могу создать репозиторий, зарегистрировать файл и затем получить журнал, который показывает, что эти события действительно произошли.

Одним из самых забавных открытий во время работы над этой частью было то, что аннотация @Rule в * JUnit *, которая во-первых, с невероятно глупым именем, потому что она действительно не заставляет вас думать о способе удаления временного файла, но также, Насколько глупо, что вы не можете сказать ему, чтобы он не удалял temp, чтобы вы могли заставить свой код работать, проверять, что у вас есть, затем снимать delete = false или что-то еще, и проходить тест? Поэтому мне пришлось дважды написать эти тесты: сначала в папку вне корневого каталога проекта, где я мог видеть, что он делает, а затем снова во временные папки. Вызовы не так уж отличаются, но есть достаточно различий, что вы не можете запустить один и тот же код на обоих.

В целом, это было довольно безболезненно, и теперь я думаю, что нет никакого вопроса о победителе консенсуса в пространстве хранилища, использование git для всех других вещей, о, и кстати, его чистая реализация на java, не составляет никакого труда ,

package com.ontometrics.spider.repository;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.NoMessageException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RepositoryTest {

	private static final Logger log = LoggerFactory.getLogger(RepositoryTest.class);

	@Rule
	public TemporaryFolder fileFolder = new TemporaryFolder();

	private File repositoryFolder;

	@Before
	public void setup() {
		repositoryFolder = fileFolder.newFolder(".git");
	}

	@Test
	public void canCreateNewRepository() throws IOException, NoHeadException, NoMessageException,
			ConcurrentRefUpdateException, JGitInternalException, WrongRepositoryStateException, NoFilepatternException {

		FileRepository repository = new FileRepositoryBuilder().setGitDir(repositoryFolder).build();
		log.info("dir: {}", repository.getDirectory());

		repository.create();
		Git git = new Git(repository);
		Git.init().call();

		RevWalk walk = new RevWalk(repository);
		RevCommit commit = null;

		File exampleHtml = new File(fileFolder.getRoot().getPath() + "examplePage.html");
		exampleHtml.createNewFile();
		FileWriter out = new FileWriter(exampleHtml);
		out.write("<html>");
		out.write("<table>");
		out.write("</table>");
		out.write("</html>");
		out.close();

		git.add().addFilepattern(".").call();
		git.commit().setMessage("Simple html file.").call();

		Iterables<RevCommit> logs = git.log().call();
		Iterator<RevCommit> i = logs.iterator();

		while (i.hasNext()) {
			commit = walk.parseCommit(i.next());
			log.info(commit.getFullMessage());

		}

		assertThat(repository, is(notNullValue()));

		repository.close();
	}

}

(Безумие, что мы не можем просто отформатировать код здесь десять лет спустя …)

Главное, на что нужно обратить внимание, это то, что вы хотите создать другой каталог для репозитория, чем для файла, который будет содержать файлы, которыми вы будете желая версии.

Следующим шагом будет сделать так, чтобы обработчик страниц паука делал различия на каждой странице, с которой он сталкивался ранее, и, если есть различия, введите новую версию. Тогда возникает вопрос: как уведомить потребителей, работающих в верхнем течении? Естественным ответом будет то, что они могут подписаться на уведомление об изменениях (Наблюдатель). Я прочитал интересную, если эффектную и непрозрачную статью на днях от какой-то * Scala * кошки о том, как * Observer * следует осудить. Мысль это было довольно слабым на самом деле. Мое чтение «Баттхед» было «слова, слова, слова» при неправильном использовании, результаты с Observer неоптимальны…, «слова, слова ….»

 

С http://www.jroller.com/robwilliams/entry/embedding_jgit_a_first_look