You are viewing [info]stas_agarkov's journal

Стас Агарков's Journal
 
[Most Recent Entries] [Calendar View] [Friends]

Below are the 20 most recent journal entries recorded in Стас Агарков's LiveJournal:

    [ << Previous 20 ]
    Sunday, December 22nd, 2019
    12:14 am
    Цитаты
    Прекрасно, что мы встретились с парадоксом. Теперь можно надеяться на продвижение вперёд. © Нильс Бор
    Если в первый момент идея не кажется абсурдной, она безнадёжна. © Альберт Эйнштейн
    Мелочи не играют решающей роли. Они решают всё. © Харви Маккей
    Настоящий друг будет с тобой, когда ты не прав, когда ты прав — любой будет с тобой. © Марк Твен
    Гений — это 1 процент вдохновения и 99 процентов труда. © Томас Эдисон
    Как видно, совершенство достигается не тогда, когда уже нечего прибавить, но когда уже ничего нельзя отнять. © Антуан де Сент-Экзюпери
    Открытое разногласие — это часто признак движения вперёд. © Махатма Ганди
    Я пришел к выводу, что понимание указателей — это не квалификация, а способность. © Джоэл Спольски
    Thursday, January 24th, 2019
    8:00 pm
    Sunday, March 20th, 2011
    8:51 pm
    Stripes Framework


    Stripes — это это фреймворк для разработки веб-приложений с открытым исходным кодом, построенный по модели MVC. Если вникнуть в его суть, то можно быстро и красиво писать web-приложения. Он предоставляет объектно-ориентированный доступ к представлениям. Рассмотрим устройство этого фреймворка на примере простого приложения для заполнения регистрационной формы. Для разработки будем использовать Intellij IDEA. Перед работой необходимо поставить плагин IntelliStripes. Создадим новый проект и поставим галочку напротив пункта Web application.
     



    Приложение Stripes состоит из нескольких основных частей:
     


    • контроллеры (ActionBean);
       
    • представления (jsp);
       
    • модели (model);
       
    • интерсепторы (Interceptor);
       
    • конвертеры типов (TypeConverter);
       
    • форматтеры (Formatter).
       

     



    Исходный код Stripes доступен и неясные моменты всегда можно прояснить с его помощью, потому что он, с моей точки зрения, очень качественный и легко расширяемый средствами ООП.
    Скачаем последнюю версию фреймворка официального сайта.
    В каталог lib кладем файлы stripes.jar и commons-logging.jar из скачанного архива и добавляем их к проекту через меню File->Project Structure->Libraries.
    Структура проекта в простейшем общем случае должна выглядеть так:
    Структура проекта

    Кроме этой библиотеки нужно добавить:


    • jstl.jar (для общей поддержки jsp);

    • standard.jar (для тега c:forEach, который мы будем использовать);

    • mail.jar (для валидации введенного email средствами stripes).



    Добавляем конфигурацию stripes в web.xml. Конфигурация представляет собой два фильтра и один маппинг фильтра:



    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
    <filter>
    <display-name>StripesFilter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
    <init-param>
    <param-name>ActionResolver.Packages</param-name>
    <param-value>com.example.action</param-value>
    </init-param>
    <init-param>
    <param-name>Extension.Packages</param-name>
    <param-value>com.example.ext</param-value>
    </init-param>
    </filter>
    <filter>
    <filter-name>DynamicMappingFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.DynamicMappingFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>DynamicMappingFilter</filter-name>
    <url-pattern>*.action</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
    </web-app>


    У фильтра StripesFilter основной параметр ActionResolver.Packages, в котором задается имя пакета, содержащего контроллеры. В параметре Extension.Packages указывается путь к дополнительным классам конвертеров типов или форматтеров, которые Stripes автоматически подключает при старте приложения.



    Создадим для контроллеров пакет com.example.action. В этом пакете создадим класс FormActionBean, который реализует интерфейс ActionBean. Контроллер с таким именем будет доступен по URL: http://localhost:8080/stripes/Form.action.



    Контроллер по мнению stripes должен реализовывать по крайней мере два метода: getContext и setContext. Создаем приватное поле context класса ActionBeanContext и делаем к нему геттер и сеттер. Очень удобно для каждого приложения создавать свой класс контекста, в котором хранить текущие параметры запроса/пользователя, например объекты DAO, параметры сессии (чтобы приведение их к типу было локализовано в одном месте). Объект контроллера и объект контекста создается новый на каждый запрос, так что они изолированы между различными запросами и пользователями.



    Создадим модель пользователя User в классе com.example.model.User. Пользователь будет иметь имя, дату рождения, электронный адрес и страну проживания. Это будет обычный бин POJO. У него обязательно должен быть публичный конструктор без параметров, если определены другие, чтобы stripes смог создать его во время стадии биндинга переменных.



    package com.example.model;

    import java.util.Date;

    public class User {

    private String name;
    private Date birthDate;
    private String email;
    private String country;

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public Date getBirthDate() {
    return birthDate;
    }

    public void setBirthDate(Date birthDate) {
    this.birthDate = birthDate;
    }

    public String getEmail() {
    return email;
    }

    public void setEmail(String email) {
    this.email = email;
    }

    public String getCountry() {
    return country;
    }

    public void setCountry(String country) {
    this.country = country;
    }

    @Override
    public String toString() {
    return name + " " + birthDate + " " + email + " " + country;
    }
    }


    Для того, чтобы данные пользователя из jsp записались в объект, нужно создать в контроллере приватное поле типа User и геттер и сеттер к нему. Код контроллера:



    package com.example.action;

    import com.example.Data;
    import com.example.model.User;
    import net.sourceforge.stripes.action.*;
    import net.sourceforge.stripes.validation.DateTypeConverter;
    import net.sourceforge.stripes.validation.EmailTypeConverter;
    import net.sourceforge.stripes.validation.Validate;
    import net.sourceforge.stripes.validation.ValidateNestedProperties;
    import java.util.Collection;

    public class FormActionBean implements ActionBean {

    private static String[] countries = new String[]{"Russia", "America"};

    @ValidateNestedProperties({
    @Validate(field = "name", required = true, minlength = 10, on = "add"),
    @Validate(field = "birthDate", converter = DateTypeConverter.class, on = "add"),
    @Validate(field = "email", required = true, converter = EmailTypeConverter.class, on = "add"),
    @Validate(field = "country", required = true, on = "add")
    })
    private User user;

    private ActionBeanContext context;

    public void setContext(ActionBeanContext actionBeanContext) {
    this.context = actionBeanContext;
    }

    public ActionBeanContext getContext() {
    return context;
    }

    @DefaultHandler
    public Resolution view() {
    return new ForwardResolution("WEB-INF/page.jsp");
    }

    public Resolution add() {
    Data.users.put(user.getName(), user);
    return new RedirectResolution(FormActionBean.class);
    }

    public User getUser() {
    return user;
    }

    public void setUser(User user) {
    this.user = user;
    }

    public String[] getCountries() {
    return countries;
    }

    public Collection<User> getUsers() {
    return Data.users.values();
    }
    }


    Разберем подробнее код контроллера. Когда пользователь делает GET-запрос по URL Form.action, Stripes вызывает обработчик по умолчанию, помеченный аннотацией @DefaultHandler. В терминологии Stripes имя обработчика называется event. Если нужно вызвать другой обработчик, то его имя должно быть одним из параметров запроса, например так: "Form.action?add=". В этом случае вызовется метод add класса FormActionBean.



    Метод getCountries нужен для вывода списка стран на странице, а метод getUsers — для вывода добавленных пользователей. С помощью класса Data мы эмулируем постоянное хранилище (типа базы данных) и для простоты используем потокобезопасный мап пользователей по имени. Код класса Data:

     

    package com.example;

    import com.example.model.User;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;

    public class Data {
    public static Map<String, User> users = Collections.synchronizedMap(new HashMap<String, User>());
    }


    Открывая страницу по адресу http://localhost:8080/stripes/Form.action мы попадаем в метод view(), который ни делает в данном случае ничего, кроме того, что передает управление коду рендеринга jsp-страницы. Код страницы page.jsp:

     


    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="s" uri="http://stripes.sourceforge.net/stripes.tld" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <html>
    <head>
    <title>Stripes Framework</title>
    </head>
    <body>
    <s:messages/>
    <s:errors/>
    <s:form beanclass="com.example.action.FormActionBean">
    Имя: <s:text name="user.name" value="Foo Bar Buzzovich"/><br/>
    Дата рождения: <s:text name="user.birthDate" value="01.01.1985"/><br/>
    Электронный адрес: <s:text name="user.email" value="user@example.com"/><br/>
    Страна: 
    <s:select name="user.country">
    <s:options-collection collection="${actionBean.countries}"/>
    </s:select><br/>
    <s:submit name="add" value="Добавить"/>
    </s:form>
    <h1>Добавленные пользователи</h1>
    <c:forEach items="${actionBean.users}" var="user">
    <s:format value="${user}"/><br/>
    </c:forEach>
    </body>
    </html>


    Stripes заменит свои теги на html, подставит текущие значения полей контроллера и их вложенных полей. Так как при вызове из метода view поле user равно null, то в форму вставятся значения по умолчанию из атрибутов value. Параметр beanclass у формы определяет метод какого класса вызывать при нажатии кнопки, помеченной как submit. В данном случае кнопка submit имеет атрибут name, равный add, что означает, что при нажатии на нее вызывется метод add и все параметры формы отправятся POST-запросом на сервер.



    POST-запрос в данном случае будет выглядеть примерно так:

     

    user.name=Foo+Bar+Buzzovich&user.birthDate=01.01.1985&user.email=user%40example.com&
    user.country=Russia&add=%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C&
    _sourcePage=WD-cxfwrDfHq57V_5vLKPcjZ6ep322Ej&__fp=HX9golAsBE8d0xswK8OSb0z4QsxFsxhx


    Перед вызовом метода add все параметры будут преобразованы в строки и объекты Java и создан объект класса User. Например, дата рождения станет из строки объектом класса Date благодаря указанию в аннтоации к этому полю параметра converter. Метод add добавит созданного Stripes пользователя в мап и сделает редирект снова на метод view: RedirectResolution вернет в браузер 302 и заголовок "Location: http://localhost:8080/stripes/Form.action".



    Если сделать запрос вида "Location: http://localhost:8080/stripes/Form.action?user=Foo%20Bar%20Buzzovich", то сработает конвертер типов для класса User:

     

    package com.example.ext;

    import com.example.Data;
    import com.example.model.User;
    import net.sourceforge.stripes.format.Formatter;
    import net.sourceforge.stripes.validation.TypeConverter;
    import net.sourceforge.stripes.validation.ValidationError;
    import java.util.Collection;
    import java.util.Locale;

    public class UserTCF implements TypeConverter<User>, Formatter<User> {

    public void setFormatType(String s) {
    }

    public void setFormatPattern(String s) {
    }

    public void init() {
    }

    public String format(User user) {
    return user.toString();
    }

    public void setLocale(Locale locale) {
    }

    public User convert(String name, Class<? extends User> aClass, Collection<ValidationError> validationErrors) {
    return Data.users.get(name);
    }
    }


    В этом классе объединены одновременно и конвертер из строки и форматтер в строку (они не обязательно должны быть противоположны по логике преобразования). Stripes увидит, что в контроллере есть поле user с именем таким же, что и в параметре запроса и вызовет конвертер типов класса этого поля, в данном случае User. Метод convert по строке найдет в мапе нужного пользователя и вернет его, и он присвоится в поле user.



    После срабатывания конвертера типов поле user уже будет заполнено нужным объектом и при отображении страницы выведутся значения из этого объекта.



    Ниже формы на jsp-странице выводится список всех уже добавленных пользователей. При этом используется тег stripes format. В данном случае, он фиктивен и вызывает метод toString объекта User, просто для примера использования форматтера.

    Monday, January 24th, 2011
    6:50 pm
    Некомпетентное руководство
    Некомпетентное руководство до добра не доводит! Это видно как на примере мелких фирм, так и крупного государства. За последнюю неделю у меня есть примеры в обоих случаях.
    Впрочем, странно было бы этому удивляться, потому что удивляются в большинстве случаев только дураки.
    Sunday, January 23rd, 2011
    12:11 am
    Правило создания обновлений для программного обеспечения
    Сегодня я понял, как нужно писать обновления для софта. При обновлении пользователю должен предлагаться выбор: устанавливать только исправления существующих багов или устанавливать также и новые функции.
    Это же очевидно как божий день.
    Friday, December 17th, 2010
    12:19 am
    РВСН
    Сегодня 51-я годовщина основания Ракетных Войск Стратегического Назначения.

    Saturday, December 4th, 2010
    12:32 pm
    Как оставить несколько значащих разрядов после запятой

    Вчера интересную задачу решали: как преобразовать double в строку оставив какое-то определенное, например равное 3, число значащих разрядов. Например, для 106,5 должна получиться строка "106", а для 2,724 — "2,72".
    Самым простым оказалось вызвать метод format с строкой формата ".2%f" и взять первые 4 символа результата, а потом проверить полученную строку: если она целиком из цифр, то взять первые три символа, а если в строке есть не только цифры (то есть там есть запятая), то вернуть все 4 символа.
    Примерный код:
        public static String formatDouble3(double a) {
            String result = String.format("%.2f", a).substring(0, 3+1);
            String result0 = result.substring(0, 3);
            if (StringUtils.isNumeric(result0)) {
                return result0;
            }
            return result;
        }
    
        public static String formatFileSize(long size) {
            String displaySize;
            if (size / FileUtils.ONE_GB > 0) {
                displaySize = formatDouble3((double)size / FileUtils.ONE_GB) + " Гб";
            } else if (size / FileUtils.ONE_MB > 0) {
                displaySize = formatDouble3((double)size / FileUtils.ONE_MB) + " Мб";
            } else if (size / FileUtils.ONE_KB > 0) {
                displaySize = formatDouble3((double)size / FileUtils.ONE_KB) + " Кб";
            } else {
                displaySize = String.valueOf(size) + " байт";
            }
            return displaySize;
        }
    
    К сожалению, стандартных способов как это сделать не нашел.
    Saturday, November 27th, 2010
    10:33 am
    Большие картинки
    По мотивам заметки, коменты к которой не позволяет оставить движок. (http://maximals.ru/notes/2010/11/27/big-sizes/)
    Ну, я, конечно, сам затестил и не могу не поделиться с вами прямо противоположными результатами.
    Сначала я хотел растянуть имеющуюся картинку. Paint сказал, что недостаточно памяти, Paint .NET завис, когда дошел до 100%. Тогда я вспомнил про гугл и взял первую попавшуюся картинку больше 70 Мп — http://upload.wikimedia.org/wikipedia/commons/b/b2/Altair-Lander.jpg. 17 125 x 9 625 пикселей.
    Опера 9.64 картинку загрузила и потом удалось успешно сохранить ее на диск, но в окне оперы был белый фон, а не картинка. ACDSee 2.5 открыла картинку мгновенно в масштабе 7% (автоматически подстраивается под размер экрана), потом увеличил нажимая + до 100%. В большом масштабе грузит примерно 1 секунду. Про Оперу плохого говорить не хочу, скорее всего у меня просто старая версия. И приятно, что мой любимый органайзер фоток хорошо работает по сравнению с XnView. :)
    Friday, November 26th, 2010
    9:58 pm
    Как сделать этот тест более адекватным?
    1. #include <stdlib.h>
    2. #include <time.h>
    3. #include <stdio.h>
    4. #include <memory.h>
    5.  
    6. size_t BLOCK_SIZE = 1536*1024*1024;
    7. int REPEAT_COUNT = 100;
    8.  
    9. int main() {
    10. void *block = malloc(BLOCK_SIZE);
    11. clock_t time1 = clock();
    12. for (int i = 0; i < REPEAT_COUNT; i++) {
    13. memset(block, 0, BLOCK_SIZE);
    14. }
    15. double duration = (double)(clock()-time1)/CLOCKS_PER_SEC;
    16. long long bytes = (long long)BLOCK_SIZE*REPEAT_COUNT;
    17. double speed = (double)bytes/duration/1024/1024;
    18. printf("%2.1f seconds\n", duration);
    19. printf("%2.1f MB/s\n", speed);
    20. return 0;
    21. }
    Friday, November 19th, 2010
    10:51 pm
    AWStats, Apache и Windows
    «Первая версия программы AWStats была разработана Лораном Дестелем, специалистом по компьютерам из Парижа. Она появилась в мае 2000 года. Дестелю было необходимо получать регулярные отчеты по статистике Web-данных компании, а поскольку требовалось приложение, имеющее большие возможности, чем те программы с открытым кодом, которые были доступны на тот момент в имеющихся источниках, он создал собственный пакет. Затем, в октябре 2000 года, Лоран разместил исходный код своей программы на языке Perl на SourceForge.net, после чего этот проект начал постоянно расширяться и совершенствоваться. В результате на сегодня продукт AWStats дорос до версии 6.95 и является на данный момент исключительно надежным и достоверным средством анализа данных журналов. AWStats способен функционировать в режиме командной строки, но может использоваться и как интерактивный компонент для Microsoft Internet Information Server (IIS). Данный продукт обеспечит создателей Web-сайта исчерпывающей информацией о том, кто посещает его.»

    Для того чтобы можно было задействовать AWStats, необходимо установить Perl.
    После установки Perl нужно загрузить AWStats.

    В процессе установки AWStats запускает собственную программу настройки, которая представляет собой Perl-приложение, запускаемое в консоли. В первую очередь программа попросит указать путь к файлу конфигурации Web-сервера. Пример строки для ввода там есть, вводить нужно без кавычек даже если в пути есть пробелы. Затем будет задан вопрос, хотите ли вы создать новый файл конфигурации для AWStats. Следует ответить Yes. Затем программа попросит указать имя того сайта, который предстоит анализировать. Программа использует это имя в качестве имени соответствующего файла конфигурации. Например, для имени сайта example.com файл конфигурации будет называться awstats.example.com.conf.
    На завершающей стадии программа настройки сообщит, что она не сможет автоматически создать задачи, выполняемые по расписанию (scheduled tasks), поскольку пакет AWStats установлен на сервер с операционной системой Windows. Сейчас нужно просто согласиться с данным сообщением, после чего программа настройки завершит свою работу. Параметры конфигурации, сформированные в этом процессе, сохраняются в соответствующем файле конфигурации: C:\Program files\AWStats\wwwroot\cgi-bin\awstats.example.com.conf.
    Программа установки модернизирует файл конфигурации apache, прописывая в него свои параметры. На этом этапе их следует вынести в отдельный виртуальный хост.

    Теперь требуется выполнить несколько дополнительных настроек в файле конфигурации awstats.example.com.conf. Первый параметр, который следует изменить, — это путь к файлам журналов. AWStats по умолчанию использует путь /var/log/httpd/mylog.log, ориентированный на UNIX-системы. Найдите в файле конфигурации параметр LogFile и установите его равным:
    LogFile="C:\Program Files\Apache Software Foundation\Apache2.2\logs\example.com-access.log",
    Затем следует сравнить формат записи лога в параметре LogFormat и в вашем файле лога. По умолчанию значение 1 параметра LogFormat соответствует combined логу веб-сервера apache.
    Кроме того, необходимо отредактировать параметры SiteDomain и HostAliases, с помощью которых, соответственно, определяется, каким образом надлежит ссылаться на данный сайт извне и каковы внутренние ссылки для этого сайта. В качестве параметра SiteDomain нужно указать основное доменное имя своего сервера или основное имя сайта в корпоративной сети. Параметр HostAliases следует отредактировать таким образом, чтобы он содержал другие допустимые доменные имена, адреса или имена виртуальных хостов, которые могут использоваться в качестве внутренних ссылок на данный сайт.

    Прежде чем продолжить, следует понять, как работает AWStats.
    При работе с AWstats мы используем два основных его компонента, а именно программу анализа и программу построения отчетов. Вся математическая обработка числовых данных выполняется программой анализа, задачей же программы построения отчетов является преобразование результатов анализа к виду интуитивно понятных отчетов в формате HTML.
    AWStats обрабатывает информацию по зарегистрированным событиям, поступающую от многих сайтов, а результаты статистического анализа данной информации заносит в файл своей внутренней базы данных. Когда мы снова запускаем программу AWStats, ей не требуется повторно анализировать ранее обработанные данные.
    Для того чтобы создать базу данных AWStats и импортировать в нее файлы журналов, следует перейти в каталог C:\Program Files\AWStats\wwwroot\cgi-bin и выполнить команду:

    awstats.pl -config=example.com -update

    Имя сайта используется программой AWStats в имени файла базы данных. Например, для сайта example.com имя соответствующего файла базы данных будет выглядеть как awstats%MM%YYYY.example.com.txt.

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

    Настало время поупражняться в построении отчетов. Необходимо перейти в каталог C:\Program Files\AWStats\tools и выполнить следующую команду:

    awstats_buildstaticpages.pl -config=example.com -update -lang=ru -dir=C:\reports -awstatsprog="C:/Program Files/AWStats/wwwroot/cgi-bin/awstats.pl"

    Обратите внимание, что каталог C:\reports должен существовать.
    Если все компоненты системы функционируют правильно, программа AWStats будет последовательно, один за другим, создавать отчеты. После окончания генерирования отчетов откройте файл C:\reports\awstats.lucidlynx.ru.html и посмотрите, что получилось.

    Эта страница содержит данные по статистике высокого уровня, а также ссылки на страницы с более подробной информацией. Например, если щелкнуть по ссылке Страны, которая находится в разделе Кто, можно перейти к таблице Страны (Топ 10). Щелкнув ссылку «Полный список» в строке заголовка данной таблицы, можно просмотреть список стран, из которых посетители обращались к вашему сайту. До данного списка можно добраться и другим способом: щелкнуть ссылку «Полный список», которая следует за ссылкой Страны в разделе Кто.

    Так, а почему в отчете нет статистики по странам? Не стоит беспокоиться, AWStats по умолчанию ведет себя подобным образом, поэтому давайте посмотрим, как мы можем это исправить.
    Чтобы установить, из какой страны обращается к сайту посетитель, применяется поиск по IP-адресу посетителя с целью выяснения полного доменного имени FQDN (Fully Qualified Domain Name), связанного с этим адресом. Если такое имя обнаруживается, то обычно оно оканчивается на что-то вроде com, .ca или .jp. По этому расширению программа AWStats определяет, из какой страны обращается данный посетитель.

    В конфигурации, установленной по умолчанию, AWStats не выполняет обратный поиск в пространстве DNS, поскольку обработка соответствующего DNS-запроса по каждому IP-адресу клиента, который обращается к данному сайту, потребует значительных временных затрат. Программа AWStats очень эффективно выполняет свои внутренние процедуры обработки данных, но в данном случае она должна отправлять запросы на внешний сервер DNS и ожидать ответа, что приводит к снижению скорости обработки.
    Тем не менее, если все-таки требуется, чтобы AWStats выполняла обратный поиск в пространстве DNS, нужно установить параметр DNSLookup равным 1 в файле конфигурации сайта. После этого программа AWStats будет выполнять обратный поиск по каждому IP-адресу в каждом вновь сформированном файле журнала при его анализе. Для всех существующих файлов журналов необходимо будет повторно выполнить процедуру анализа. Для этого требуется удалить существующий файл базы данных AWStats, после чего вновь запустить процедуру анализа. При этом AWStats будет посылать запрос обратного поиска DNS по каждой записи, и нужно быть готовым к тому, что это займет много времени. По завершении данной процедуры следует создать отчеты повторно, и в них появятся данные по странам для обращавшихся на сайт посетителей.

    Для запуска ло расписанию, можно поставить программу nnCron и добавить задачу с вызовом

    awstats.pl -config=example.com -update

    например, каждый час.

    Основано на Анализ статистики с помощью AWStats
    Sunday, November 7th, 2010
    7:14 pm
    Задачи
    1. Какие проблемы Вы видите в этом коде?
    public class Singleton {
      private static Singleton instance;
    
      private Singleton() {}
    
      public static Singleton getInstance() {
        if (instance == null) {
          synchronized(Singleton.class) {
            if (instance == null) {
              instance = new Singleton();
            }
          }
        }
        return instance;
      }
    }
    

    Ответ:
    Две лишних проверки instance на null, которые не нужны. Синхронизация по объекту класса объекта бессмысленна здесь, так как создать экземплара синглтона логичнее проще сделать в строке private static Singleton instance = new Singleton(); И, самое главное, это будет быстрее работать, чем проверки каждый раз при вызове метода getInstance.


    2. Какие из следующих стандартных контейнеров позволяют найти в них элемент (по его значению) за O(ln(n))?
  • java.util.Vector<E>
  • java.util.ArrayList<E>
  • java.util.LinkedList<E>
  • java.util.TreeSet<E>
  • java.util.HashSet<E>
  • сортированный java.util.Vector<E>
  • сортированный java.util.ArrayList<E>
  • сортированный java.util.LinkedList<E>

    Ответ:
  • java.util.TreeSet<E>
  • сортированный java.util.Vector<E>, только если он упорядочен
  • сортированный java.util.ArrayList<E>, только если он упорядочен


    3. Напишите реализацию класса с неблокирующим методом BigInteger next(), который возвращает элементы последовательности: [1, 2, 4, 8, 16, ...]. Код должен корректно работать в многопоточной среде.

    Ответ:
    public class TwoSequence {
        private static final BigInteger TWO = new BigInteger("2");
        private static BigInteger current = new BigInteger("1");
    
        public static BigInteger next() {
            BigInteger currentRet = current;
            synchronized (TWO) {
                current = current.multiply(TWO);
            }
            return currentRet;
        }
    }
    

    4. В рамках системы распределённого планировщика заданий существует таблица, описывающая каждое задание:

    CREATE TABLE task (
        id BIGINT AUTO_INCREMENT,
        created_time TIMESTAMP,
        state INT,
        flags BIGINT, -- Битовая маска
        task VARCHAR(40),
        PRIMARY KEY(id)
    )
    

    Заданий может быть очень много. К таблице существует три типа запросов:

  • Найти все новые задания (created_time > '<время>')
  • Найти все новые задания, для которых задан определённый state
  • Найти все новые задания, у которых установлен определённый флаг

    Напишите SQL-выражения для этих запросов. Как можно было бы изменить схему таблицы, чтобы запросы работали быстрее при условии, что ресурсы системы ограничены?

    Ответ:
    Для ускорения нужно добавить ключ на поле created_time и разнести флаги по разным полям и на каждое поле навесить ключ.
    select * from task where created_time > ?
    select * from task where (created_time > ?) and (state = ?)
    select * from task where (created_time > ?) and (flags & ? <> 0), где знак вопроса это битовая маска определенного флага
  • Wednesday, October 27th, 2010
    12:47 am
    Установка Web-сервера
    Установка Apache

    Установка PHP

    Установка MySQL


    Установка Apache



    Устанавливаем Apache HTTP Server в каталог C:\Apache\. (нужно изменить папку)
    При установке необходимо указать Network Domain, Server Name и Administrator's Email Address.
    Можно ввести любые значения, главное - чтобы они не были пустыми.

    Открываем файл C:\Apache\Apache2\conf\httpd.conf. Находим в нем строку

    DocumentRoot "C:/Apache/Apache2/htdocs"

    и меняем ее на

    DocumentRoot "D:/Web".

    После этого создаем каталог D:/Web и перезагружаем Apache (в меню левой кнопки мыши на значке Apache в трее).

    Для указания какие файлы по умолчанию искать в каталоге, если имя файла не задано,
    находим в файле C:\Apache\Apache2\conf\httpd.conf строку

    DirectoryIndex index.html index.html.var

    и добавляем в нее нужные имена файлов, например так:

    DirectoryIndex index.html index.html.var index.php.

    После этого, как и после любого изменения конфигурации Apache, веб-сервер Apache требует перезагрузки.

    Установка PHP



    Устанавливаем PHP в каталог C:\PHP\. При установке выбираем имеющийся у нас веб-сервер, в данном случае Apache 2.2.x Module.
    Также нужно выбрать модули (Extensions): GD2 и MySQL.
    Далее указываем конфигурационный каталог Apache — C:\Apache\Apache2\conf\.
    При запросе на изменение конфигурации Apache, отвечаем Yes.

    Для проверки работоспособности PHP можно создать в каталоге D:/Web файл index.php следующего содержания:

    <?php
    	phpinfo();
    ?>
    

    Введите в браузере адрес http://localhost/
    и вы должны увидеть страницу с информацией о версии PHP.
    Далее в файле php.ini нужно найти строки upload_tmp_dir и session.save_path и поменять их значения соответственно на D:\Web\Temp\php\upload и D:\Web\Temp\php\session (эти папки нужно создать).

    Установка MySQL



    Устанавливаем MySQL Server в каталог C:\MySQL\.
    Во время установки пропускаем подключение к аккаунту MySQL.com.
    После установки запускается мастер конфигурации MySQL.
    Выбираем детальную конфигурацию, машину разработчика, многофункциональную базу данных,
    путь к БД оставляем по умолчанию (Installation Path), далее — все по умолчанию до настройки кодировок.
    Выбираем Best Support For Multilingualism. В следующем окне все по умолчанию
    и ставим галочку Include Bin Directory in Windows PATH.
    В окне настроек безопасности снимаем галочку Modify Security Settings.

    Копируем в каталог D:\Web каталог phpMyAdmin и набираем в браузере адрес
    http://localhost/phpmyadmin/.
    Должна открыться система управления СУБД MySQL phpmyadmin.

    Стас Агарков © 2007, ага, а потом и zeff ©

    Tuesday, October 19th, 2010
    9:03 pm
    Как улучшить продуктивность
    В самом начале XX столетия Чарльз Шваб, президент компании «Bethlehem Steel», встретился с консультантом в области общественных отношений и управления Ивом Ли, так как хотел улучшить продуктивность своей компании. «Мы знаем, что нам следовало бы сделать, — объяснил Шваб, — но если вы можете показать нам лучший способ достижения этого, то я вас с удовольствием выслушаю и заплачу любую сумму в пределах разумного».

    Ли сказал, что может помочь и что это займет всего 20 минут. Он протянул Швабу чистый лист бумаги и сказал: «Напишите шесть самых важных дел, которые вы должны сделать завтра». Шваб сделал, как ему было сказано.

    «Теперь пронумеруйте их в соответствии с их важностью для вас и для компании». Когда Шваб закончил, Ли продолжил: «А теперь положите эту бумагу в карман, и первое, что вы должны сделать завтра утром, это вытащить этот лист бумаги и посмотреть на пункт номер 1. Не смотрите на другие пункты — только на первый, и начинайте работать согласно ему и до тех пор, пока он не будет полностью выполнен. Затем приступайте таким же образом к пункту 2, потом к пункту 3 и т. д., пока ваш рабочий день не подойдет к концу. Не беспокойтесь, если успеете справиться только с одним или двумя пунктами. Зато вы будете работать над самыми важными. Без них вы все равно не выполнили бы остальные. А не имея четко составленной системы, вы, вероятно, потратили бы в десять раз больше времени на их завершение — и, возможно, выполняли бы их не в порядке важности.

    Делайте так каждый рабочий день, — сказал Ли. — После того как вы убедитесь в ценности этой системы, посоветуйте и вашим служащим попробовать делать то же самое. Испытывайте этот метод столько времени, сколько захотите, а потом пришлите мне чек на такую сумму, которую, по вашему мнению, заслуживает эта идея».

    Через несколько недель Шваб прислал Ли чек на 25.000 долларов вместе с письмом, где говорилось, что это самый полезный урок, который он когда-либо получал. И очень скоро после этого компания «Bethlehem Steel» стала самым крупным независимым производителем стали своего времени.
    Saturday, October 16th, 2010
    12:23 pm
    bit-statistics
    По просьбе MaximAL-а сделал программу для вычисления количества единичных бит в файле и для поиска подстроки в файле.
    Написал на Си, надеюсь что кроссплатформенно. Лежит на code.google.com.
    Скорость вычисления количества единичных бит — 250 Мб/сек, скорость поиска подстроки алгоритмом Кнута-Морриса-Пратта — 100 Мб/сек.
    Friday, October 8th, 2010
    9:56 pm
    Два бага email2trac или пятница прошла не зря
    Есть такой замечательный скрипт email2trac, который занимается тем, что преобразует входящие письма в тикеты и комментарии trac. Но есть в нем одна проблема: комментарии, созданные им, имеют неверный номер в своем якоре.

    Автор скрипта думал, что у нового комментария номер должен быть равен количеству уже существующих комментариев: то есть, если в базе 3 комментария, то новый комментарий должен иметь номер 3. Очевидно, что забыли добавить +1. В результате я создал тикет. Я подумал, что баг исправлен, но не тут-то было.

    Оказывается, что это не все, и номера иногда по-прежнему ставятся неправильно.
    Через полтора часа отладки, я выяснил, что он ставит номер комментария, читая количество изменений в таблице ticket_change, используя стандартное api trac: Ticket.get_changelog. Автор, конечно, же рассуждал логично, но он не знал, что trac на изменение, например, полей тикета создает N записей в этой таблице: по одной на каждое поле и плюс одна запись на сам комментарий.

    Но ведь сам trac правильно ставит номера! Тогда я стал искать в нем, где он определяет реальное число комментариев. И такое место я нашел довольно быстро. Это был метод TicketModule.grouped_changelog_entries, который возвращает список изменений, в котором одновременные изменения сгруппированы в одно, то есть так, как это отображается в самом trac. trac рендерит именно то, что возвращается этим методом. По итогам создал еще один тикет.

    Update: Автор принял мой патч.
    Wednesday, September 15th, 2010
    2:57 am
    Как установить trac
    Команды для установки trac:
    sudo aptitude install python-setuptools
    sudo easy_install trac==0.12
    sudo aptitude install libapache2-mod-wsgi
    sudo aptitude install python-mysqldb
    sudo adduser trac --system
    sudo aptitude install mysql-server
    

    После этого в mysql нужно выполнить:
    create database trac default charset utf8;
    grant all on trac.* to trac@localhost identified by 'trac123';
    

    После этого выполнить:
    sudo -u trac trac-admin /home/trac/projects initenv
    
    и ввести имя проекта "My Project" и строку подключения: " mysql://trac:trac123@localhost:3306/trac".


    Затем нужно создать файл:
    sudo -u trac vim /home/trac/trac.wsgi
    
    со следующим содержимым:
    import os
    os.environ['TRAC_ENV'] = '/home/trac/projects'
    
    import trac.web.main
    
    application = trac.web.main.dispatch_request
    

    и файл:
    sudo vim /etc/apache2/sites-available/trac
    
    с содержимым:
    WSGIScriptAlias /trac /home/trac/trac.wsgi
    
    <Directory /home/trac/projects>
            WSGIApplicationGroup %{GLOBAL}
            Order deny,allow
            Allow from all  
    </Directory>
    
    <Location /trac/login>
            AuthType Basic
            AuthName "Trac"
            AuthUserFile /home/trac/.htpasswd
            Require valid-user
    </Location>
    

    Для включения созданного сайта нужно выполнить команду:
    sudo a2ensite trac
    

    После этого нужно создать пользователя командой:
    sudo -u trac htpasswd -cm /home/trac/.htpasswd stas
    

    Чтобы дать права администратора trac, нужно выполнить в базе trac команду:
    insert into permission values('stas', 'TRAC_ADMIN');
    

    Для того чтобы trac мог менять конфигурацию из web-интерфейса, нужно выполнить следующие команды:
    sudo chmod g+w /home/trac/projects/conf/trac.ini
    sudo chown :www-data /home/trac/projects/conf/trac.ini
    sudo chmod g+w /home/trac/projects/conf
    sudo chown :www-data /home/trac/projects/conf
    

    Для применения всех настроек нужно перезапустить apache:
    sudo /etc/init.d/apache2 restart
    
    Wednesday, September 8th, 2010
    8:32 pm
    Отсутствие типизации
    Целый день боролся с языками с динамической типизацией. Раньше считал, и всё больше укрепляюсь в мысли, что это дерьмо.

    Рассмотрим пример кода на языке php:
    if (sizeof(explode("/", $string) == 1)) {
        print($string."\n");
    }
    


    И аналогичный пример кода на java:
    String string = "my";
    if (sizeof(explode("/", string) == 1)) {
        System.out.println("1");
    }
    


    В первом случае код не подсвечивается как неверный, ну и ладно бы: падал бы в runtime, но он и выполняется правильно с точки зрения компьютера, но не с точки зрения алгоритма, который я пишу... В случае явы код даже не компилируется.

    Так как в яве функций sizeof и explode нет, то я сделал их, для примера, такими:
    private static int sizeof(String[] array) {
        return 0;
    }
    
    private static String[] explode(String delimiter, String string) {
        return new String[]{};
    }
    


    Идем дальше. Попробуем сделать такой вызов функции strpos:
    if (strpos("string", "str") == false) {
        print("yes");
    }
    

    Как вы думаете будет ли выведено yes? Ответ: будет! Функция вернет 0, как ей и полагается, но false это в php и нуль тоже. Хотя strpos, судя по документации, должна возвращать false только если подстрока не найдена.
    Да, посмотрев, чуть ниже, в описание этой функции, видим предупреждение о том, что для проверки на false надо использовать оператор ===. Но это ведь совсем неочевидно.

    Знаете, что? Да пошел на хуй этот язык.
    Monday, May 24th, 2010
    8:30 pm
    Об открытых и закрытых дверях
    Если публикация прямых ссылок на нелегальный контент является способствованием распространению нелегального контента, то открытая дверь в квартиру способствует увеличению уровня квартирных краж и должна наказываться в такой же степени.
    Sunday, May 2nd, 2010
    9:38 am
    Wednesday, March 24th, 2010
    11:31 pm
    Мой ответ Чемберлену
    Прочитал сегодня статью Почему гику стоит переходить на Linux. В статье приведены типичные в таких случаях аргументы. Ниже я приведу свои объективные аргументы.

    На Windows у меня была 30-гигабайтная папка «D:\Distribs». Софт в ней устаревал с пугающей скоростью.
    Слово «пугающей» излишне эмоционально. На самом деле большинство программ не нужно сильно часто обновлять. А те, которые нужно, сами знают об этом и обновляются сами. А обновления в репозиториях проходят очень долго.

    И большая часть оттуда вообще никогда не запускалась, а лежала на всякий случай.
    А причем здесь Windows? Удали эти программы.

    С подтягиванием всех зависимостей, справочными файлами и настройками GUI при необходимости.
    MSI-файлы (да и обычные exe-инсталляторы) Windows тоже содержат все необходимое.

    Вам не придется общаться с многошаговым инсталлятором, в котором все равно подавляющее большинство людей просто давят «Дальше».
    При установке MySQL, например, спрашивается пароль к базе в интерактивном режиме. Но, согласен, большинство вопросов при установке программ Windows - лишние. Но опять же это не проблема Windows: сейчас многие программы уже задают по минимому вопросов.

    А удалить программу можно также просто, одной командой, без всего этого ужаса со специальными деинсталляторами, мусором в реестре и куче других мест по системе.
    В Windows при удалении тоже спрашивается один раз подтверждение и всё. Если в ее менеджерре пакетов в панели управления выбрать кнопку Удалить. Кроме того, база apt тоже существует. Просто вы про нее не пишете, а про реестр пишете. Но ни то, ни другое не должно волновать пользователя.

    Если в каком-нибудь приложении вдруг найдется серьезная уязвимость, в течении дней или даже часов, вам предложат его обновить.
    За 9 месяцев использования Linux на работе никогда не видел, чтобы появлялись предложения обновить какое-то стороннее программное обеспечение, кроме Firefox и IntelljIdea. Ну, а они и в Windows предлагают обновиться.

    В случае же с Windows, вам придется самостоятельно обновлять какой-нибудь Acrobat Reader, через дырку в котором работают десятки червей. В лучшем случае, программа сама умеет проситься в интернет на предмет обновиться.
    А Adobe сделала патч к нему? Если да, то почему не встроила автоматическое обновление в свой Reader? Опять возникает вопрос: при чем здесь Windows?

    В Windows меня бесили диски. C:\ D:\ и прочие смайлики в начале пути, кажущиеся такими естественными для привычного Windows пользователя, на взгляд человека, видевшего альтернативу, являются настоящим бредом!
    Слишком эмоциональное и не обоснованное заявление.

    Разложить информацию с дисков в нужном вам виде.
    То, что написано в этом предложении вполне можно сделать и в Windows.

    Так же много раз у меня происходила интересная вещь — буквы дисков менялись местами.
    Не сталкивался с этой проблемой с 2001 года, когда вышла Windows XP.

    Дальше — больше. Linux имеет некоторые рекомендации по поводу того, где программы должны хранить свои файлы. В сруктуре файловой системы есть некоторые стандартные пути, которые имеют заранее известное предназначение.
    Описанное вами есть и в Windows. А если программы не умеют этим пользоваться, то, опять же, это не проблемы Windows.

    В Windows же было полнейшее разнообразие. Некоторые приложения норовили нагадить прямо под себя в Program Files, некоторые — и того хуже — в C:\Windows!
    Почему вы пишете что в Windows твориться безобразие, а потом что гадят какие-то программы третьих сторон? Причем здесь Windows? Этот вопрос не переставал у меня возникать на протяжении всего чтения вашей статьи.

    Часть файлов хранилась по ужасному пути «C:\Documents and settings\User\Application data», часть — на уровень выше.
    Я не видел ни одной программы, которая хранит файлы в каталоге C:\Documents and settings\User, кроме файлов реестра пользователя самой Windows и кошелька Webmoney. Опять же это проблемы Webmoney, а не Windows.

    Некоторые программы вообще использовали, прости Господи, реестр.
    Здесь согласен с вами. Реестр — это ужасно.

    При этом, переустановка системы, которая проводилась обычно с очисткой системного раздела, приводила к потере всех этих настроек и данных, если не знать где они живут и как их спасти.
    Уже давно достаточно скопировать настройки из той самой Documents and settings для сохранения настроек приложений после переустановки Windows. Я трачу на переустановку Windows и всех приложений не более 2 часов.

    В Linux же мы имеем стройную и безусловно работающую концепцию домашних каталогов, где программа хранит все, что ей важно.
    В Windows тоже самое.

    Для каждого пользователя каталог свой.
    В Windows тоже для каждго пользователя каталог свой.

    У каждого пользователя программа будет вести себя так, как хотел этот пользователь.
    В Windows — тоже самое. А если какая-то программа ведет себя не так, то это, само собой, проблема программы, а не операционной системы Windows.

    В Windows, вроде бы, тоже можно каким-то образом делать символические ссылки, но это опять же тайные знания. Простые пользователи дальше ярлыков не заходят.

    Простые пользователи Linux тоже не создают символических ссылок, иначе какие же это простые пользователи? Команду их создания нужно узнать и выучить. В Windows — тоже самое.

    В моем случае это сначала был Windows(Total) Commander, потом Far.
    Я 9 месяцев пользуюсь Ubuntu, но до сих пор считаю, что управление файлами удобнее производить из Total Commander. Но, соглашусь, не все операции. Некоторые удобнее через консоль.

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

    Для примера, создайте в Проводнике или Far стандартную структуру maven-проекта:
    ...
    Наверняка для этого есть средства, может даже встроенные. Но я, в свое время, с такими простыми вещами мучился.

    Такие задания выполняют на уроках дети в 11 классе. Я не знаю, какой уровень развития нужно иметь, чтобы мучиться с таким простым заданием.

    А в консоли Linux это будет одна команда:
    mkdir -p project/src/{main,test}/{java,resources}

    Спасибо! Я до сих пор не знал о таких возможностях этой команды.

    Многие вещи, делаются из консоли гораздо быстрее, чем через GUI.
    Обычно гораздо больше времени уходит на то, чтобы подумать, как сделать, а не на то, чтобы побыстрее набрать команду в консоли, сделать 10 опечаток, исправить их и нажать наконец Enter.

    От использования консоли в Windows также отталкивает совершенно непрозрачная работа переменных окружения.
    В чем это заключается?

    Мне редко удавалось добиться того, чтобы $PATH работала так, как должна.
    Приведите пример. Я никогда за более чем 10 лет не испытывал никаких проблем, которые я мог бы причислить к ошибкам Windows.

    Часто, чтобы запустить команду из консоли, приходилось писать полный путь до бинарника.
    Добавьте каталог к ней в PATH. Или покажите точную последовательность действий для воспроизведения той ошибки, о которой вы говорите.

    В Linux с этим все замечательно.
    В Windows тоже :)

    В Windows есть Запланированные задания.
    Согласен, cron лучше. Но к самой Windows это не относится. Это всего лишь одна из встроенных программ.

    Я однажды даже пытался ими воспользоваться. У меня не получилось :) Оно просто не нашло бинарника, который надо запустить.
    Вы полагаете, что это Windows? Я очень в этом сомневаюсь. Скорее всего. вы сами что-то не поняли, не так сделали или испортили. Или приведите точную последовательность действий, приводящую к ошибке.

    Про удаленное управление полностью с вами согласен.

    Про поддержку устройств тоже. В Windows и Linux все устройства давно прекрасно поддерживаются.

    Про документацию не совсем согласен. По проблемам Windows тоже полно форумом, блогов и wiki.

    Учите английский :) Возможно, придется много читать. Читать хорошую, полезную документацию на английском.
    Необходимость учить язык я считаю минусом Linux.
[ << Previous 20 ]
Сайт Стаса Агаркова   About LiveJournal.com