Ярлыки

.htaccess (4) тестирование (8) шаблоны проектирования (3) css (5) Debian (6) docker (2) Doctrine2 (6) Git (6) html (4) java (6) javascript (13) jquery (11) LFS (3) linux (23) mac os (4) mod_rewrite (2) MSSQL (4) MySQL (18) ORM Doctrine (17) patterns (3) PDO (3) perl (7) PHP (64) PHPUnit (8) Python (15) SEO (2) Silex (1) SimpleXML (1) SQL (14) ssh (4) Ubuntu (24) Yii1 (1) Zend Framework (19) ZendFramework2 (8)

среда, 30 марта 2011 г.

Python. Apache CGI.

Подключаем модули apache cgi, cgid

$ sudo a2enmod имя_модуля

В каталоге для cgi-скриптов создаем файл .htaccess

Allow from all

Options +ExecCGI
AddHandler cgi-script .py

В этом каталоге создаем cgi-скрипт hello.py

#!/usr/bin/python
print "Content-Type: text/plain\n\n"

print "Hello, cgi world!"

Будет работать, если хостинг позволяет (зависит от настроек apache).
Подробно читать тут http://httpd.apache.org/docs/2.0/howto/htaccess.html

Права на каталог должны быть не ниже 755 (права выставляем в консоли сервера с chmod), а права на файл 777

вторник, 29 марта 2011 г.

Johnny Cash - Solitary Man

Python. Работа со временем.

from datetime import datetime, timedelta
import time

# дата час назад от текущего момента
past_hour = datetime.now() + timedelta(hours = -1)

# дата неделю назад от текущего момента
past_week = datetime.now() + timedelta(weeks = -1)

# unix timestamp
timestamp =  time.mktime(past_week.timetuple())

year = 1979
month = 3
day = 31
timestamp = time.mktime((year, month, day, 0, 0, 0, -1, -1, -1))

Python. Объединение списка в строку с разделителем.

tmp = ['a', 'b', 'c']
str = ",".join(tmp)
'a,b,c'

Python. Форматирование даты (аналог date() в php)

>>> import time
>>> timestamp = 1284375159
>>> time.strftime("%m %d %Y",time.localtime(timestamp))
'09 13 2010'

Python. Работа с MySQL.

Установка модуля python в Ubuntu:

sudo apt-get install python-mysqldb

import MySQLdb

conn = MySQLdb.connect (host = "localhost",
                           user = "db_user",
                           passwd = "db_pwd",
                           db = "db_name")
cursor = conn.cursor ()
cursor.execute ("SELECT VERSION()")
row = cursor.fetchone ()
print "server version:", row[0]
cursor.close ()
conn.close ()


Если получаем ошибку вида:
...
ImportError: No module named MySQLdb
чаще всего это значит, что в системе несколько версий пайтон и скрипт использует ту для которой не установлен модуль ...

Обработка исключений:

try:
    conn = MySQLdb.connect (host = "localhost",
                             user = "testuser",
                             passwd = "testpass",
                             db = "test")
except MySQLdb.Error, e:
    print "Error %d: %s" % (e.args[0], e.args[1])
    sys.exit (1)


Выборка:

cursor.execute ("SELECT row1, row2 FROM my_table")
while (1):
    row = cursor.fetchone ()
    if row == None:
        break
    print "%s, %s" % (row[0], row[1])
print "Number of rows returned: %d" % cursor.rowcount


Вставка:

cursor.execute ("""
    INSERT INTO animal (name, category)
    VALUES
        ('snake', 'reptile'),
        ('frog', 'amphibian'),
        ('tuna', 'fish'),
        ('racoon', 'mammal')
""")

понедельник, 28 марта 2011 г.

Python. Обработка исключений (пример).

import sys
import traceback

...

try:
    resumes.append(resume.get_data())

# В методе get_data() возникает исключение
except Exception:
    registry = Registry()

    log = open(registry.get('error_log') , 'a')
    log.write("\nresume #" + resume_id + " error\n")

    # Пишем трассировку исключения в лог
    print traceback.print_exc(file = log)

    if(registry.is_debug_mode()):
        print traceback.print_exc(file = sys.stdout)

    log.close()

...


Класс Registry (упрощенный, для реальной работы не подходит)
По поводу реализации singletone в python см. http://code.activestate.com/recipes/66531/

class Registry:
    __shared_state = {}

    vars = {}

    def __init__(self):
        self.__dict__ = self.__shared_state

    def set(self, key, value):
        self.vars[key] = value

    def get(self, key):
        return self.vars[key]

    def is_debug_mode(self):
        if(self.vars['mode'] == 'debug'):
            return True
        return False

Python. Удаление дубликатов значений из списка (аналог array_unique в php).

mylist = list(set(mylist))

Python. Регулярные выражения и группировка результата.

re.findall(r"\w","abcdefg")

# Возвращает список
# ('a','b','c','d','e','f','g',)

вторник, 22 марта 2011 г.

Python. Регулярные выражения. Нежадный поиск с учетом служебных символов.

html = '<div><span>hello</span><span>goodbay</span></div>'

# (?=...) - соотв, если ... соотв тому, что идет следом, но не включает ...
# (?<=...) - соотв, если ... предшествует искомой позиции, но не включает ...
# .*? - нежадный поиск, произвольная последовательность символов
# re.DOTALL - включить в поиск служебные символы (перенос строк, пробелы и тд)

match = re.search('(?<=<span>).*?(?=</span>)', html, re.DOTALL).group(0)
print match
'hello'
Поиск в строке с кодировкой utf-8
m = re.search('some string'.decode('utf-8'), 'some string and something else'.decode('utf-8'), re.UNICODE|re.IGNORECASE)

понедельник, 21 марта 2011 г.

Python. Парсер HTML BeautifulSoup. Поиск по атрибуту CSS class.

soup.find("b", { "class" : "lime" })

Python. Сериализация объектов.

import pickle

def serialize(output, obj):
    file = open(output, 'wb')
    pickle.dump(obj, file)
    file.close()

def unserialize(output):
    file = open(output, 'rb')
    obj = pickle.load(file)
    file.close()

    return obj
obj = ['a', 'b', 'c']
output = 'dump.txt'
serialize(output, obj)

obj = unserialize(output)

Python. Работа со строками в utf-8.

Локализация системы (Ubuntu) - UTF-8. Во всех модулях указываем кодировку в начале файла:
# -*- coding: utf-8 -*- 

# Строка в кодировке windows-1251
str = ' привет '
# Кодируем в юникод
str = str.decode('windows-1251')

# strip - Удаляем начальный и завершающий пробел
# replace - Ищем и заменяем строку (u'' - строка в юникод)
# encode - кодируем в набор байт перед выводом на печать, в файл и тд
str = str.strip().replace(u'пр', '').encode('utf8')

'ивет'

воскресенье, 20 марта 2011 г.

Python. Подключение модуля из произвольного каталога.

dirFoo\
    Foo.py
    dirBar\
        Bar.py 
 
import sys
sys.path.append( <path to dirFoo> )
import Bar
 

Mac OS. Установка Python 3.2 в Tiger 10.4.11.

Качаем и устанавливаем пакет для соотв. версии Mac OS X с сайта python, в моем случае это

Python 3.2 Mac OS X 32-bit i386/PPC Installer (for Mac OS X 10.3 through 10.6)

$ which python
/usr/bin/python
$ which python3.2
/Library/Frameworks/Python.framework/Versions/3.2/bin/python3.2
$ echo $PATH
/Library/Frameworks/Python.framework/Versions/3.2/bin:/bin:/sbin:/usr/bin:/usr/sbin

Далее настраивает ide, в моем случает это Netbeans 6.8
Добавляем новый путь к Python в свойствах проекта

пятница, 18 марта 2011 г.

PHP. ucfirst и utf-8.

function ucfirst_utf8($string, $e ='utf-8') {
    if (function_exists('mb_strtoupper') && function_exists('mb_substr') && !empty($string)) {
        $string = mb_strtolower($string, $e);
        $upper = mb_strtoupper($string, $e);
        preg_match('#(.)#us', $upper, $matches);
        $string = $matches[1] . mb_substr($string, 1, mb_strlen($string, $e), $e);
    } else {
        $string = ucfirst($string);
    }
    return $string;
}

Python. Для чего нужно 'if __name__ == "__main__"' ?

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

Пример, есть два файла:

$ cat mymath.py
def square(x):
    return x * x

if __name__ == '__main__':
    print "test: square(42) ==", square(42)

$ cat mygame.py
import mymath

print "this is mygame."
print mymath.square(17)

$ python mymath.py
test: square(42) == 1764

$ python mygame.py
this is mygame.
289

четверг, 17 марта 2011 г.

MySQL. Настройка и оптимизация MySQL сервера.

В этой статье будут описаны различные настройки MySQL, преимущественно те, которые влияют на производительность. Для удобства все переменные разделены по разделам (базовые настройки, ограничения, настройки потоки, кэширование запросов, тайминги, буферы, InnoDB). Сначала уточним имена некоторых переменных, которые изменились в версии 4 MySQL, а в сети продолжают встречаться и старые и новые варианты имен, что вызывает вопросы.

Итак, в 4 версии у ряда переменных появилось окончание _size. Это касается переменной thread_cache_size и переменных из раздела Буферы. А переменная read_buffer_size до версии 4 называлась record_buffer. Также переменная skip_external_locking из раздела Базовые настройки до версии 4 называлась skip_locking.
Переменные делятся на две основных категории: переменные со значениями и переменные-флаги. Переменные со значениями записываются в конфигурационном файле в виде variable = value, а переменные-флаги просто указываются. Также вы наверное заметили, что в некоторых случаях в названиях переменных используется "-", а в некоторых "_". Переменные с дефисом являются стартовыми опциями сервера и их нельзя изменить при работе сервера (при помощи SET); переменные с подчеркиванием являются опциями работы сервера и их возможно изменять на лету. Если речь идет о «переменной состояния» или рекомендуется наблюдать за значением переменной, название которой записано в виде Variable_Name, то следует выполнять запрос SHOW STATUS LIKE "Variable_Name" для получения значения этой переменной, либо заглянуть на вкладку состояние в phpMyAdmin, где дополнительно будут комментарии по значению этой переменной.
А теперь займемся описанием переменных и их возможными значениями.

Базовые настройки


  • low-priority-updates — эта опция снижает приоритет операций INSERT/UPDATE по сравнению с SELECT. Актуально, если данные важно быстрее прочитать, чем быстрее записать.
  • skip-external-locking — опция установлена по умолчанию, начиная с версии 4. Указывает MySQL-серверу не использовать внешние блокировки при работе с базой. Внешние блокировки необходимы в ситуациях, когда несколько серверов работают с одними и теми же файлами данных, т.е. имеют одинаковую datadir, что на практике не используется.
  • skip-name-resolve — не определять доменные имена для IP-адресов подключающихся клиентов. При этом пользовательские разрешения нужно настраивать не на хосты, а на IP-адреса (за исключением localhost). Если вы соединяетесь с сервером только с локальной машины, то особого значения не имеет. Для внешних соединений ускорит установку соединения.
  • skip-networking — не использовать сеть, т.е. вообще не обрабатывать TCP/IP соединения. Общение с сервером при этом будет происходить исключительно через сокет. Рекомендуется, если у вас нет ПО, которое использует только TCP/IP для связи с сервером.

Ограничения


  • bind-address — интерфейс, который будет слушать сервер. В целях безопасности рекомендуется установить здесь 127.0.0.1, если вы не используете внешние соединения с сервером.
  • max_allowed_packet — максимальный размер данных, которые могут быть переданы за один запрос. Следует увеличить, если столкнетесь с ошибкой «Packet too large».
  • max_connections — максимальное количество параллельных соединений к серверу. Увеличьте его, если сталкиваетесь с проблемой «Too many connections».
  • max_join_size — запрещает SELECT операторы, которые предположительно будут анализировать более указанного числа строк или больше указанного числа поисков по диску. Используется для защиты от кривых запросов, которые пытаются считать миллионы строк. Значение по умолчанию более 4 миллиардов, поэтому вы скорее всего захотите его значительно уменьшить.
  • max_sort_length — указывает, сколько байт из начала полей типа BLOB или TEXT использовать при сортировке. Значение по умолчанию 1024, если вы опасаетесь некорректно спроектированных таблиц или запросов, то следует его уменьшить.

Настройки потоков


  • thread_cache_size — указывает число кэшируемых потоков. После обработки запроса сервер не будет завершать поток, а разместит его в кэше, если число потоков, находящих в кэше меньше, чем указанное значение. Значение по умолчанию 0, увеличьте его до 8 или сразу до 16. Если наблюдается рост значения переменной состояния Threads_Created, то следует еще увеличить thread_cache_size.
  • thread_concurrency — актуально только для Solaris/SunOS вопреки тому, что пишут в сети. «Подсказывает» системе сколько потоков запускать одновременно, выполняя вызов функции thr_setconcurrency. Рекомендованное значение — двойное или утроенное число ядер процессора.

Кэширование запросов


  • query_cache_limit — максимальный размер кэшируемого запроса.
  • query_cache_min_res_unit — минимальный размер хранимого в кэше блока.
  • query_cache_size — размер кэша. 0 отключает использование кэша. Для выбора оптимального значения необходимо наблюдать за переменной состояния Qcache_lowmem_prunes и добиться, чтобы ее значение увеличивалось незначительно. Также нужно помнить, что излишне большой кэш будет создавать ненужную нагрузку.
  • query_cache_type — (OFF, DEMAND, ON). OFF отключает кэширование, DEMAND – кэширование будет производиться только при наличии директивы SQL_CACHE в запросе, ON включает кэширование.
  • query_cache_wlock_invalidate — определяет будут ли данные браться из кеша, если таблица, к которым они относятся, заблокирована на чтение.

Кэш запросов можно представить себе как хэш-массив, ключами которого являются запросы, а значениями — результаты запросов. Кроме результатов, MySQL хранит в кэше список таблиц, выборка из которых закэширована. Если в любой из таблиц, выборка из которой есть в кэше, проиcходят изменения, то MySQL удаляет из кэша такие выборки. Также MySQL не кеширует запросы, результаты которых могут измениться.
При запуске MySQL выделяет блок памяти размером в query_cache_size. При выполнении запроса, как только получены первые строки результата сервер начинает кэшировать их: он выделяет в кэше блок памяти, равный query_cache_min_res_unit, записывает в него результат выборки. Если не вся выборка поместилась в блок, то сервер выделяет следующий блок и так далее. В момент начала записи MySQL не знает о размере получившейся выборки, поэтому если записанный в кэш размер выборки больше, чем query_cache_limit, то запись прекращается и занятое место освобождается, следовательно, если вы знаете наперед, что результат выборки будет большим, стоит выполнять его с директивой SQL_NO_CACHE.

Тайминги


  • interactive_timeout — время в секундах, в течение которого сервер ожидает активности со стороны интерактивного соединения (использующего флаг CLIENT_INTERACTIVE), прежде чем закрыть его.
  • log_slow_queries — указывает серверу логировать долгие («медленные») запросы (выполняющиеся дольше long_query_time). В качестве значения передается полное имя файла (например /var/log/slow_queries).
  • long_query_time — если запрос выполняется дольше указанного времени (в секундах), то он будет считаться «медленным».
  • net_read_timeout — время в секундах, в течение которого сервер будет ожидать получения данных, прежде чем соединение будет прервано. Если сервер не обслуживает клиентов с очень медленными или нестабильными каналами, то 15 секунд здесь будет достаточно.
  • net_write_timeout — время в секундах, в течение которого сервер будет ожидать получения данных, прежде чем соединение будет прервано. Если сервер не обслуживает клиентов с очень медленными или нестабильными каналами, то 15 секунд здесь будет достаточно.
  • wait_timeout — время в секундах, в течение которого сервер ожидает активности соединения, прежде чем прервет его. В общем случае 30 секунд будет достаточно.

Буферы


У всех буферов есть общая черта — если из-за установки большого размера буфера данные будут уходить в файл подкачки, то от буфера будет больше вреда, чем пользы. Поэтому всегда ориентируйтесь на доступный вам объем физической ОЗУ.
  • key_buffer_size — размер буфера, выделяемого под индексы и доступного всем потокам. Весьма важная настройка, влияющая на производительность. Значение по умолчанию 8 МБ, его однозначно стоит увеличить. Рекомендуется 15-30% от общего объема ОЗУ, однако нет смысла устанавливать больше, чем общий размер всех .MYI файлов. Наблюдайте за переменными состояния Key_reads и Key_read_requests, отношение Key_reads/Key_read_requests должно быть как можно меньше (< 0,01). Если это отношение велико, то размер буфера стоит увеличить.
  • max_heap_table_size — максимальный допустимый размер таблицы, хранящейся в памяти (типа MEMORY). Значение по умолчанию 16 МБ, если вы не используете MEMORY таблиц, то установите это значение равным tmp_table_size.
  • myisam_sort_buffer_size — размер буфера, выделяемого MyISAM для сортировки индексов при REPAIR TABLE или для создания индексов при CREATE INDEX, ALTER TABLE. Значение по умолчанию 8 МБ, его стоит увеличить вплоть до 30-40% ОЗУ. Выигрыш в производительности соответственно будет только при выполнении вышеупомянутых запросов.
  • net_buffer_length — объем памяти, выделяемый для буфера соединения и для буфера результатов на каждый поток. Буфер соединения будет указанного размера и буфер результатов будет такого же размера, т.е. на каждый поток будет выделен двойной размер net_buffer_length. Указанное значение является начальным и при необходимости буферы будут увеличиваться вплоть до max_allowed_packet. Размер по умолчанию 16 КБ. В случае ограниченной памяти или использования только небольших запросов значение можно уменьшить. В случае же постоянного использования больших запросов и достаточного объема памяти, значение стоит увеличить до предполагаемого среднего размера запроса.
  • read_buffer_size — каждый поток при последовательном сканировании таблиц выделяет указанный объем памяти для каждой таблицы. Как показывают тесты, это значение не следует особо увеличивать. Размер по умолчанию 128 КБ, попробуйте увеличить его до 256 КБ, а затем до 512 КБ и понаблюдайте за скоростью выполнения запросов типа SELECT COUNT(*) FROM table WHERE expr LIKE "a%"; на больших таблицах.
  • read_rnd_buffer_size — актуально для запросов с "ORDER BY", т.е. для запросов, результат которых должен быть отсортирован и которые обращаются к таблице, имеющей индексы. Значение по умолчанию 256 КБ, увеличьте его до 1 МБ или выше, если позволяет память. Учтите, что указанное значение памяти также выделяется на каждый поток.
  • sort_buffer_size — каждый поток, производящий операции сортировки (ORDER BY) или группировки (GROUP BY), выделяет буфер указанного размера. Значение по умолчанию 2 МБ, если вы используете указанные типы запросов и если позволяет память, то значение стоит увеличить. Большое значение переменной состояния Sort_merge_passes указывает на необходимость увеличения sort_buffer_size. Также стоит проверить скорость выполнения запросов вида SELECT * FROM table ORDER BY name DESC на больших таблицах, возможно увеличение буфера лишь замедлит работу (в некоторых тестах это так).
  • table_cache (table_open_cache с версии 5.1.3) — количество кэшированных открытых таблиц для всех потоков. Открытие файла таблицы может быть достаточно ресурсоемкой операцией, поэтому лучше держать открытые таблицы в кэше. Следует учесть, что каждая запись в этом кэше использует системный дескриптор, поэтому возможно придется увеличивать ограничения на количество дескрипторов (ulimit). Значение по умолчанию 64, его лучше всего увеличить до общего количества таблиц, если их количество в допустимых рамках. Переменная состояния Opened_tables позволяет отслеживать число таблиц, открытых в обход кэша, желательно, чтобы ее значение было как можно ниже.
  • tmp_table_size — максимальный размер памяти, выделяемой для временных таблиц, создаваемых MySQL для своих внутренних нужд. Это значение также ограничивается переменной max_heap_table_size, поэтому в итоге будет выбрано минимальное значение из max_heap_table_size и tmp_table_size, а остальные временные таблицы будут создаваться на диске. Значение по умолчанию зависит от системы, попробуйте установить его равным 32 МБ и понаблюдать за переменной состояния Created_tmp_disk_tables, ее значение должно быть как можно меньше.

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

InnoDB


  • innodb_additional_mem_pool_size — размер памяти, выделяемый InnoDB для хранения различных внутренних структур. Если InnoDB будет недостаточно этой памяти, то будет запрошена память у ОС и записано предупреждение в лог ошибок MySQL.
  • innodb_buffer_pool_size — размер памяти, выделяемый InnoDB для хранения и индексов и данных. Значение — чем больше, тем лучше. Можно увеличивать вплоть до общего размера всех InnoDB таблиц или до 80% ОЗУ, в зависимости от того, что меньше.
  • innodb_flush_log_at_trx_commit — имеет три допустимых значения: 0, 1, 2. При значении равном 0, лог сбрасывается на диск один раз в секунду, вне зависимости от происходящих транзакций. При значении равном 1, лог сбрасывается на диск при каждой транзакции. При значении равном 2, лог пишется при каждой транзакции, но не сбрасывается на диск никогда, оставляя это на совести ОС. По умолчанию используется 1, что является самой надежной настройкой, но не самой быстрой. В общем случае вы можете смело использовать 2, данные могут быть утеряны лишь в случае краха ОС и лишь за несколько секунд (зависит от настроек ОС). 0 — самый быстрый режим, но данные могут быть утеряны как при крахе ОС, так и при крахе самого сервера MySQL (впрочем данные лишь за 1-2 секунды).
  • innodb_log_buffer_size — размер буфера лога. Значение по умолчанию 1 МБ, увеличивать его стоит, если вы знаете, что будет большое количество транзакций InnoDB или если значение переменной состояния Innodb_log_waits растет. Вам вряд ли придется увеличивать его выше 8 МБ.
  • innodb_log_file_size — максимальный размер одного лог-файла. При достижении этого размера InnoDB будет создавать новый файл. Значение по умолчанию 5 МБ, увеличение размера улучшит производительность, но увеличит время восстановления данных. Установите это значение в диапазоне 32 МБ — 512 МБ в зависимости от размера сервера (оценив его субъективно).

Также для мониторинга работы сервера удобно использовать phpMyAdmin, интерес представляют вкладки Состояние и Переменные. Дополнительно phpMyAdmin дает советы по тюнингу тех или иных переменных в зависимости от параметров работы сервера.

оригинал

вторник, 15 марта 2011 г.

Ubuntu. Adobe Flash 64-bit для Firefox 64-bit.

Не знаю почему раньше не сделал... Теперь все работает в разы лучше.

sudo apt-get purge flashplugin-nonfree flashplugin-installer gnash gnash-common mozilla-plugin-gnash swfdec-mozilla
 sudo rm -f /usr/lib/firefox-addons/plugins/libflashplayer.so
 sudo rm -f /usr/lib/mozilla/plugins/libflashplayer.so
 sudo rm -f /usr/lib/mozilla/plugins/flashplugin-alternative.so
 sudo rm -f /usr/lib/mozilla/plugins/npwrapper*flash*so
 rm -f ~/.mozilla/plugins/*flash*so

 sudo apt-add-repository ppa:sevenmachines/flash
 sudo apt-get update
 sudo apt-get install flashplugin64-installer

Если надо удалить:
sudo apt-get remove flashplugin64-installer

пятница, 11 марта 2011 г.

Javascript. ООП.

function someObj() {

    this.publicVar = 'public';

    var privateVar = 'private';

    this.someMethod = function() {

        alert('boo');

    }

}


o_obj = new someObj();

alert(o_obj.publicVar); //сообщит "public"

o_obj.publicVar = 'new';

alert(o_obj.publicVar); //сообщит "new"

alert(o_obj.privateVar); //ничего не сообщит или будет ошибка, зависит от браузера

o_obj.someMethod(); //сообщит "boo"

Jquery. $.param() - создание строки параметров запроса для URL.

Пример:

var params = { width:1680, height:1050 };
var str = jQuery.param(params);
$("#results").text(str); 
 
Результат:

width=1680&height=1050

среда, 9 марта 2011 г.

Java. Вложенные классы.

Что такое вложенный класс?

Судя по названию, вложенный класс в языке Java является классом, объявленным внутри другого класса. Вот простой пример:
public class EnclosingClass {
    ...
    public class NestedClass {
    ...
    }
}
Вы можете определить вложенный класс со спецификаторами доступа public, private или protected. Можно также определить вложенный класс с ключевыми словами final (для предотвращения его изменения), abstract (означает, что для него нельзя создать экземпляр) или static.

Любой вложенный класс имеет доступ ко всем членам окружающего его класса, даже если они объявлены со спецификатором доступа private.

Определение вложенных классов


Вложенный класс определяется также как и не вложенный класс, но делается это внутри другого класса. Для некоторого вымышленного примера давайте определим класс Wallet (кошелек) внутри класса Adult. Хотя в реальной жизни вы могли бы иметь объект Wallet отдельно от Adult, это не было бы так полезно; вполне логично, чтобы каждый Adult имел Wallet (или, по крайней мере, что-нибудь для хранения денег, а MoneyContainer звучит немного странно). Также имеет смысл, чтобы Wallet не существовал в Person, поскольку Baby не имеют его, а если бы он был в Person, его наследовали бы все подклассы Person.

Наш класс Wallet будет очень простым, поскольку служит только для демонстрации определения вложенного класса:

protected class Wallet {
protected ArrayList bills = new ArrayList();

    protected void addBill(int aBill) {
        bills.add(new Integer(aBill));
    }

    protected int getMoneyTotal() {
        int total = 0;
        for (Iterator i = bills.iterator(); i.hasNext(); ) {
             Integer wrappedBill = (Integer) i.next();
             int bill = wrappedBill.intValue();
             total += bill;
        }
        return total;
    }
}

Мы определим этот класс внутри класса Adult следующим образом:
public class Adult extends Person {
  protected Wallet wallet = new Wallet();
    public Adult() {
  }
  public void talk() {
    System.out.println("Spoke.");
  }
  public void acceptMoney(int aBill) {
    this.wallet.addBill(aBill);
  }
  public int moneyTotal() {
    return this.wallet.getMoneyTotal();
  }
  protected class Wallet {
  ...
  }
}

После определения нашего вложенного класса и нового метода acceptMoney() мы можем использовать их следующим образом:
Adult anAdult = new Adult();
anAdult.acceptMoney(5);
System.out.println("I have this much money: " + anAdult.moneyTotal());

Упрощенная обработка событий

Язык Java поддерживает подход к обработке событий с использованием соответствующих классов, что позволяет создавать и обрабатывать ваши собственные события. Но обработка событий может быть намного проще. Все, что вам действительно нужно, - это некоторая логика для генерирования "события" (которое на самом деле вовсе не обязательно должно быть классом event) и некоторая логика для прослушивания этого события и соответствующего реагирования. Например, предположим, что во время любого перемещения Person наша система генерирует (или инициирует) MoveEvent, которое мы можем выбрать для обработки, а можем и не выбрать. Это потребует нескольких изменений в нашей системе. Мы должны:

  • Создать класс "приложение" для запуска нашей системы и демонстрации использования анонимного внутреннего класса.
  • Создать интерфейс MotionListener, который наше приложение может реализовать, и затем обработать событие в прослушивателе.
  • Добавить список (List) прослушивателей в Adult.
  • Добавить метод addMotionListener() к Adult для регистрации прослушивателя.
  • Добавить метод fireMoveEvent() к Adult, для того чтобы он мог сказать прослушивателям, когда обрабатывать событие.
  • Добавить код в наше приложение для создания объекта Adult и регистрации его в качестве обработчика.

Это все не сложно. Вот класс Adult с новой функциональностью:
public class Adult extends Person {
  protected Wallet wallet = new Wallet();
  protected ArrayList listeners = new ArrayList();
  public Adult() {}
  public void move() {
    super.move(); fireMoveEvent();
  }
  ...
  public void addMotionListener(MotionListener aListener) {
    listeners.add(aListener);
  }
  protected void fireMoveEvent() {
    Iterator iterator = listeners.iterator();
    while(iterator.hasNext()) {
      MotionListener listener = (MotionListener) iterator.next();
      listener.handleMove(this);
    }
  }
  protected class Wallet {
  ...
  }
}
Обратите внимание на то, что мы сейчас переопределили move(). Сначала вызываем move() класса Person, затем вызываем fireMoveEvent() для того, чтобы указать прослушивателям среагировать на событие. Мы также добавили addMotionListener(), который добавляет MotionListener к списку прослушивателей. Вот как выглядит MotionListener:
public interface MotionListener {
 public void handleMove(Adult eventSource);
}
Все, что осталось сделать - создать наш класс приложения:
public class CommunityApplication implements MotionListener {
  public void handleMove(Adult eventSource) {
    System.out.println("This Adult moved: \n" + eventSource.toString());
  }
  public static void main(String[] args) {
    CommunityApplication application = new CommunityApplication();
    Adult anAdult = new Adult();
    anAdult.addMotionListener(application);
    anAdult.move();
  }
}
Этот класс реализует интерфейс MotionListener. Это означает, что он реализует handleMove(). Здесь мы просто выводим сообщение для демонстрации генерирования события.

Анонимные внутренние классы

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

Вы можете преобразовать приведенный на предыдущей странице пример для использования анонимного внутреннего класса, изменив вызов addMotionListener() в CommunityApplication.main() следующим образом:
anAdult.addMotionListener(new MotionListener() {
   public void handleMove(Adult eventSource) {
      System.out.println("This Adult moved: \n" + eventSource.toString());
   }
});
Вместо реализации классом CommunityApplication интерфейса MotionListener мы объявили неименованный (и, следовательно, анонимный) внутренний класс типа MotionListener и определили реализацию метода handleMove(). Тот факт, что MotionListener является интерфейсом, а не классом, не имеет значения. И то, и другое допустимо.

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

Использование вложенных классов

Вложенные классы могут быть очень полезными. Но они также могут вызывать головную боль.

Используйте вложенный класс в тех случаях, когда не имеет большого смысла определять класс вне окружающего класса. В нашем примере мы могли бы безболезненно определить класс Wallet вне класса Adult. Но представьте себе что-то подобное классу Personality (личность). Вы когда-нибудь видели его вне экземпляра Person? Нет, поэтому есть веские причины определить его в виде вложенного класса. Хорошим практическим правилом является следующее: вы должны определять класс не вложенным, пока не станет очевидным, что он должен быть вложенным. В этом случае выполните рефакторинг и вложите его в другой класс.

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

пятница, 4 марта 2011 г.

Java. Использование сервлетов в Ubuntu.

/var/lib/tomcat6/webapps/myapp/ - каталог в котором лежит веб-приложение

Переменная окружения (путь к jvm):
export JAVA_HOME=/usr/lib/jvm/java-6-sun

Создаем каталог для сервлетов:
$ sudo mkdir /var/lib/tomcat6/webapps/myapp/WEB-INF
$ sudo mkdir /var/lib/tomcat6/webapps/myapp/WEB-INF/classes


Собственно сам класс-сервлет (откомпилированный класс должен находиться в каталоге classes):
// HelloWorld.java

import java.io.*;

import javax.servlet.http.*;
import javax.servlet.*;

public class HelloWorld extends HttpServlet 
{
    public void doGet (HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException 
    {
      PrintWriter out = res.getWriter();

      out.println("<h1>Hello, world!</h1>");
      out.close();
    }
}

Компиляция:

javac -classpath "/usr/share/tomcat6/lib/servlet-api.jar" HelloWorld.java

В каталоге WEB-INF создаем файл web.xml (дескриптор поставки), в котором регистрируем сервлет:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" 
   "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
     <servlet>
         <servlet-name>hello</servlet-name>
         <servlet-class>HelloWorld</servlet-class>
    </servlet>

    <servlet-mapping>
         <servlet-name>hello</servlet-name>
         <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app> 
 
Теперь сервлет доступен по адресу:
http://localhost:8080/myapp/hello 

четверг, 3 марта 2011 г.

Java. Сравнение строк.

String str1 = "a";
String str2 = "a";
if(str1 == str2)
    out.println("equals");

Если же создаем два разных объекта, то такая конструкция работать не будет (буду сравниваться указатели на объекты). Поэтому следует использовать метод:

String str1 = new String("a");
String str2 = new String("a");
if(str1.equals(str2))
    out.println("equals");

вторник, 1 марта 2011 г.

Ubuntu. Установка apache tomcat6.

sudo apt-get install tomcat6

Это установит сервер Tomcat только со встроенным web-приложением ROOT, которое выводит простейшую страницу "It works".

Теперь по адресу http://localhost:8080 мы должны увидеть:


Конфигурационные файлы Tomcat находятся в /etc/tomcat6.

По умолчанию Tomcat 6.0 запускает соединение HTTP на порту 8080 и соединение AJP на порту 8009. Вы, возможно, захотите изменить эти порты по умолчанию с тем, чтобы избежать конфликтов с другим сервером в системе. Это делается изменением следующих строк в /etc/tomcat6/server.xml:


<connector connectiontimeout="20000" port="8080" protocol="HTTP/1.1" redirectport="8443" />
...
<connector port="8009" protocol="AJP/1.3" redirectport="844" />

По умолчанию Tomcat сначала работает с OpenJDK-6, затем пробует Sun's JVM, а затем все остальные JVM. Если у Вас установлены разные JVM, Вы можете указать, какую из них использовать, путем установки JAVA_HOME в /etc/default/tomcat6:

JAVA_HOME=/usr/lib/jvm/java-6-sun 

По-умолчанию корневая директория сервера:
/var/lib/tomcat6/webapps/ 

Подробнее