Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

Vista filesystem wierdness

Что за ерунда?!

Vista filesystem wierdness

Java SE Development Kit 6 Update 4

Вышла Java SE Development Kit 6 Update 4. Download link.


ЗЫ: Поздравляю себя с сотенкой постов. 100 posts

Easy reporting to Word, PDF, HTML

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

Я считаю, что дизайнер стоит кучу денег, для его написания нужен не один человеко-год. И пока что не видел ни одного универсального решения, с которым пользователь ощущал бы себя не обезьяной. Наиболее приемлемый вариант - вбивать логику построения отчета в программу. Дешево и сердито! Ну, дальше изложу один из вариантов такого решения.

Для получения отчета нужно:

  1. Данные

    Строки, числа, даты необходимые для построения отчета.

  2. Шаблон

    Набор правил построения отчета.

  3. Процессор

    Механизм, преобразующий данные по правилам.

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

XML, XSLT 2.0 и Altova StyleVision

Итак, данные в формате XML преобразуются XSLT процессором в выходной файл.

Altova StyleVision является нашим самым главным фигурантом и помощником, он же developer-friendly designer. Это визуальный редактор, который позволяет получить на выходе XSLT 2.0 файл, преобразующий XML в HTML, RTF или PDF!

Я уже точно не помню, зачем понадобился именно XSLT 2.0, вероятно из-за каких-то фич дизайнера, но вот найти бесплатный процессор для Java было напряжно. Из коммерческих есть Oracle XML Developer's Kit, из некоммерческих - Saxon. Других не нашел! С .NET ситуация хуже, под него только Saxon. Ну, Saxon так Saxon!

Исходные данные

Исходные данные получаем из источника данных (БД, например) и формируем XML файл с определенной схемой, которая описывается в XSD. Механизм формирования не имеет значения, но, если данные качаются из БД, я бы предпочел возложить этот процесс на сервер (далее в примере я буду получать от любимой СУБД Oracle сразу XML, а если поднатужиться, то и файл с отчетом можно получить!).

Я буду использовать существующую схему SH (Sales History). Как ее установить написано здесь.

Итак, наш отчет будет выдавать некую информацию о клиенте - имя, фамилия, пол, дата рождения и т.д. StyleVision хочет видеть XSD схему документов, которые прийдется использовать в качестве данных. Схему можно создавать либо вручную, подгоняя потом под нее XML, либо построить по существующему XML. Все это, кстати умеет делать Altova XMLSpy. Вот моя схема (Customer.xsd):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:simpleType name="T_Valid">
      <xs:restriction base="xs:string">
         <xs:enumeration value="I"/>
         <xs:enumeration value="A"/>
      </xs:restriction>
   </xs:simpleType>
   <xs:simpleType name="T_Gender">
      <xs:restriction base="xs:string">
         <xs:enumeration value="M"/>
         <xs:enumeration value="F"/>
      </xs:restriction>
   </xs:simpleType>
   <xs:complexType name="T_Customer">
      <xs:sequence>
         <xs:element ref="FirstName"/>
         <xs:element ref="LastName"/>
         <xs:element ref="Gender"/>
         <xs:element ref="YearOfBirth"/>
         <xs:element ref="Address"/>
         <xs:element ref="Valid"/>
      </xs:sequence>
   </xs:complexType>
   <xs:complexType name="T_Address">
      <xs:sequence>
         <xs:element ref="Street"/>
         <xs:element ref="PostalCode"/>
         <xs:element ref="City"/>
         <xs:element ref="StateProvince"/>
         <xs:element ref="Country"/>
      </xs:sequence>
   </xs:complexType>
   <xs:element name="YearOfBirth" type="xs:short"/>
   <xs:element name="Valid" type="T_Valid"/>
   <xs:element name="Street" type="xs:string"/>
   <xs:element name="StateProvince" type="xs:string"/>
   <xs:element name="PostalCode" type="xs:int"/>
   <xs:element name="LastName" type="xs:string"/>
   <xs:element name="Gender" type="T_Gender"/>
   <xs:element name="FirstName" type="xs:string"/>
   <xs:element name="Customer" type="T_Customer"/>
   <xs:element name="Country" type="xs:string"/>
   <xs:element name="City" type="xs:string"/>
   <xs:element name="Address" type="T_Address"/>
</xs:schema>

Соответственно, Oracle SQL запрос (в SQL Server этот запрос был бы на пару страниц неразбираемого текста):

SELECT XMLElement("Customer",
                  XMLElement("FirstName", c.cust_first_name),
                  XMLElement("LastName", c.cust_last_name),
                  XMLElement("Gender", c.cust_gender),
                  XMLElement("YearOfBirth", c.cust_year_of_birth),
                  XMLElement("Address",
                             XMLElement("Street", c.cust_street_address),
                             XMLElement("PostalCode", c.cust_postal_code),
                             XMLElement("City", c.cust_city),
                             XMLElement("StateProvince", c.cust_state_province),
                             XMLElement("Country",
                                        (SELECT country_name FROM sh.countries WHERE country_id = c.country_id)
                             )
                  ),
                  XMLElement("Valid", c.cust_valid)) AS DATA
  FROM sh.customers c
 WHERE c.cust_id = 43228

Вместо 43228, конечно, нужно сделать bindable переменную, но мне лень.

И его результат (Customer.xml):

<Customer>
   <FirstName>Abner</FirstName>
   <LastName>Everett</LastName>
   <Gender>M</Gender>
   <YearOfBirth>1957</YearOfBirth>
   <Address>
      <Street>117 West Gloucester Avenue</Street>
      <PostalCode>72059</PostalCode>
      <City>Los Angeles</City>
      <StateProvince>CA</StateProvince>
      <Country>United States of America</Country>
   </Address>
   <Valid>I</Valid>
</Customer>

Altova StyleVision

File -> New -> New from XML Schema / DTD..., указать путь к XSD файлу и желательно предоставить ему сразу файл-пример с данными, соответствующий этой схеме, конечно же.

После окончания создания шаблона (template.sps) нужно его сохранить в XSLT (transform-rtf.xslt): File -> Save Generated Files -> Save Generated XSLT-RTF File....

Saxon

What is Saxon?

The Saxon package is a collection of tools for processing XML documents. The main components are:

  • An XSLT 2.0 processor, which can be used from the command line, or invoked from an application, using a supplied API. This can also be used to run XSLT 1.0 stylesheets.
  • An XPath 2.0 processor accessible to applications via a supplied API.
  • An XQuery 1.0 processor that can be used from the command line, or invoked from an application by use of a supplied API.
  • An XML Schema 1.0 processor. This can be used on its own to validate a schema for correctness, or to validate a source document against the definitions in a schema. It is also used to support the schema-aware functionality of the XSLT and XQuery processors. Like the other tools, it can be run from the command line, or invoked from an application.
  • On the Java platform, when using XSLT, XPath, or XML schema validation, the API supported by Saxon is the JAXP API, which means it is possible for a Java application to switch between different XSLT, XPath, and XML Schema processors without changing the application code. At the time of writing, however, Saxon is still the only JAXP implementation that offers support for XSLT 2.0. Saxon offers two APIs for XQuery processing: its own native API, which has been present in the product for some time, and an early implementation of the XQJ specification which is under development as part of the Java Community Process.
  • On the .NET platform, Saxon offers an API that enables close integration with other services available from .NET, notably the XML-related classes in the System.Xml namespace. It isn't possible to use Saxon as a transparent plug-in replacement for the System.Xml.Xsl processor, because the API for the Microsoft engine using concrete classes rather than abstract interfaces. However, it is possible to use it as a functional replacement with minor changes to your application code.

Saxon implements XSLT 2.0, XPath 2.0, and XQuery 1.0 as defined in the final Recommendations of 23 January 2007. It also includes an implementation of the XML Schema 1.0 Recommendation.

Full details of Saxon's conformance to the specifications are provided in the Conformance section.

In addition, Saxon provides an extensive library of extensions, all implemented in conformance with the XSLT and XQuery Recommendations to ensure that portable stylesheets and queries can be written. These include the EXSLT extension libraries common, sets, math, and dates-and-times. Many of these extensions were pioneered in Saxon and have since become available in other products.

These extension functions are in general accessible from XQuery and XPath as well as XSLT, except where they depend on stylesheet information. Some extensions are available in Saxon-SA only.

Компиляция XSLT файла

Библиотека Saxon позволяет компилировать XSLT файл:

The term compile is stretching a point. The executable that is produced does not contain machine instructions, or even interpreted Java bytecode. It contains instructions in the form of a data structure that Saxon itself can interpret. Note that the format of compiled stylesheets is unlikely to be stable from one Saxon release to the next.

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

java -cp d:\Development\Libs\Java\Saxon.v9\saxon9.jar net.sf.saxon.Compile transform-rtf.xslt transform-rtf.saxon

transform-rtf.xslt - входной файл, transform-rtf.saxon - выходной.

Java код

Все просто - подключение к БД, выполнение запроса, получение потока XML, загрузка скомпилированного шаблона, создание трансформера и преобразование XML в RTF.

 0: /**
 1:  *
 2:  */
 3: package com.chabster.reporting.app;
 4:
 5: import java.io.File;
 6: import java.io.InputStream;
 7: import java.sql.Connection;
 8: import java.sql.DriverManager;
 9:
10: import javax.xml.transform.*;
11: import javax.xml.transform.stream.StreamResult;
12: import javax.xml.transform.stream.StreamSource;
13:
14: import net.sf.saxon.Configuration;
15: import net.sf.saxon.PreparedStylesheet;
16: import oracle.jdbc.OraclePreparedStatement;
17: import oracle.jdbc.OracleResultSet;
18: import oracle.jdbc.driver.OracleDriver;
19: import oracle.sql.OPAQUE;
20:
21: /**
22:  * @author Chabster
23:  */
24: public class Startup
25: {
26:
27:   static String query = "SELECT XMLElement(\"Customer\",\r\n" +
28:         "                 XMLElement(\"FirstName\", c.cust_first_name),\r\n" +
29:         "                 XMLElement(\"LastName\", c.cust_last_name),\r\n" +
30:         "                 XMLElement(\"Gender\", c.cust_gender),\r\n" +
31:         "                 XMLElement(\"YearOfBirth\", c.cust_year_of_birth),\r\n" +
32:         "                 XMLElement(\"Address\",\r\n" +
33:         "                            XMLElement(\"Street\", c.cust_street_address),\r\n" +
34:         "                            XMLElement(\"PostalCode\", c.cust_postal_code),\r\n" +
35:         "                            XMLElement(\"City\", c.cust_city),\r\n" +
36:         "                            XMLElement(\"StateProvince\", c.cust_state_province),\r\n" +
37:         "                            XMLElement(\"Country\",\r\n" +
38:         "                                       (SELECT country_name FROM sh.countries WHERE country_id = c.country_id)\r\n" +
39:         "                            )\r\n" +
40:         "                 ),\r\n" +
41:         "                 XMLElement(\"Valid\", c.cust_valid)) AS DATA\r\n" +
42:         "  FROM sh.customers c\r\n" +
43:         " WHERE c.cust_id = 43228";
44:
45:   public static void main(String[] args) {
46:      try {
47:         DriverManager.registerDriver(new OracleDriver());
48:
49:         Connection conn = DriverManager.getConnection("jdbc:oracle:thin:chabster/***@localhost:1521/MAIN");
50:         OraclePreparedStatement preparedStmt = (OraclePreparedStatement) conn.prepareStatement(query);
51:
52:         OracleResultSet oraRS = (OracleResultSet) preparedStmt.executeQuery();
53:         oraRS.next();
54:         OPAQUE xmlObj = oraRS.getOPAQUE(1);
55:         Configuration configuration = new Configuration();
56:         InputStream xmlStream = xmlObj.getStream();
57:
58:         PreparedStylesheet preparedStylesheet = PreparedStylesheet.loadCompiledStylesheet(configuration, "transform-rtf.saxon");
59:
60:         Transformer transformer = preparedStylesheet.newTransformer();
61:         File outputFile = new File("out.rtf");
62:         Source xmlSource = new StreamSource(xmlStream);
63:         Result output = new StreamResult(outputFile);
64:
65:         transformer.transform(xmlSource, output);
66:
67:         xmlStream.close();
68:         oraRS.close();
69:         preparedStmt.close();
70:         conn.close();
71:      }
72:      catch (Exception ex) {
73:         ex.printStackTrace();
74:      }
75:   }
76: }

Выводы

Механизм прост и удобен для разработчика. Генерация файлов происходит быстро, поддерживаются основные форматы документов - HTML, RTF и PDF. Правда, есть и свои нюансы, связанные с ограниченными возможностями дизайнера Altova StyleVision. Например, если в отчет нужно вставить картинку из БД, то прийдется это писать частично вручную. Также минимальный размер генерируемого файла - 46 Kb! Плюс вечно плавающие стандарты W3C и отсутствие библиотек, которые их поддерживают.

WL-500gP HP 2015 problems

Нудавно я писал про подключение HP LaserJet 2015 к WL-500gP. Ну, вот и нашлись проблемы:

  1. Печать PostScript не работает.

    При печати через драйвер PostScript рутер пишеть в лог такие строчки:

    [lpr.notice] p9100d[88]: Connection from 192.168.1.4 port 51240 accepted
    [kern.warning] kernel: printer.c: usblp0: nonzero read/write bulk status received: -71
    [kern.err] kernel: printer.c: usblp0: error -71 reading printer status
    
  2. Печать больших документов не работает. Даже через PCL 5/6.

    В логе анологичные строки и я подозреваю, что это, как и в первом случае, связанно с нехваткой оперативной памяти устройства.

Что тут еще говорить? Попытаюсь еще через CUPS и HPLIP, но последний не удалось скомпилировать. Да, все из-за той же нехватки оперативной памяти.

Я уже себе с трудом представляю, как рутер способен работать при 1-2 Mb свободной памяти. Это конкретно сужает область его задач.

Bosses and Jeeps

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

И еще Тарас, мой сотрудник, сегодня отмочил:

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

Hacking WL-500gP Part 1

Ну, вот я и добрался до своего ненаглядного рутера WL500gP. За последние пару дней на плечи ему свалилось немало:

  1. SSH
  2. 4 Gb Flash
  3. Установка пакетов
  4. syslog-ng
  5. Создание пользователей
  6. VSFTPD
  7. Samba 3
  8. Cron
  9. Firewall

Все это поверх прошивки от Олега. Рекомендую глянуть на инструкцию по установке. Итак, по порядку.

Подготовка

Возможность управлять загрузкой рутера по-сути одна - файлы /usr/local/sbin/post-*, которые выполняются в таком порядке:

  1. post-firewall

    Выполняется сразу после инициализации сетевых устройств. Скрипту система передает параметры - WAN-интерфейс, WAN-адрес, Bridge-интерфейс, Bridge-адрес:

    /usr/local/sbin/post-firewall vlan1 wan.ip.ad.dr br0 lan.ip.ad.dr
    
  2. post-boot

    Параметрами не интересовался.

  3. pre-mount

    Параметрами не интересовался. Выполняется только, если есть что монтировать.

  4. post-mount

    Параметрами не интересовался. Выполняется только, если есть что монтировать.

  5. pre-shutdown (при перегрузке рутера)

    Параметрами не интересовался.

Я создал файл /usr/local/sbin/post-include с полезными переменными такого содержания:

#!/bin/sh

self=`basename $0`
log="/usr/bin/logger -t $self"

Изначально каждый файл /usr/local/sbin/post-* у меня выглядит так:

#!/bin/sh

. "`dirname $0`/post-include"

$log 'Begin'

# initialization here

$log 'End'

А /usr/local/sbin/pre-shutdown так:

#!/bin/sh

. "`dirname $0`/post-include"

[ -x /opt/bin/logger ] && log="/opt/bin/logger -p Syslog.Alert"

$log 'Shutting down system.'

# BEGIN services termination
if [ `pidof vsftpd` -ne "" ]; then
        $log 'Terminating vsftpd'
        /bin/kill `pidof vsftpd`
fi

if [ `pidof smbd` -ne "" ]; then
        $log 'Terminating smbd'
        /bin/kill `pidof smbd`
fi
if [ `pidof nmbd` -ne "" ]; then
        $log 'Terminating nmbd'
        /bin/kill `pidof nmbd`
fi

if [ -f /var/run/lighttpd.pid ]; then
        $log 'Terminating lighhttpd'
        /bin/kill `cat /var/run/lighttpd.pid`
fi

if [ -f /opt/var/run/cron.pid ]; then
        $log 'Terminating cron'
        /bin/kill `cat /opt/var/run/cron.pid`
fi

if [ -f /var/run/syslog-ng.pid ]; then
        $log 'Terminating syslog-ng'
        /bin/kill `cat /var/run/syslog-ng.pid`
fi

if [ -f /var/run/dropbear.pid ]; then
        $log 'Terminating dropbear'
        /bin/kill `cat /var/run/dropbear.pid`
fi

# END services termination

if [ -d /tmp/harddisk/opt ]; then
        $log 'Unmounting FlashFS'
        /bin/umount /tmp/harddisk
        /bin/umount /opt
fi

pre-shutdown аккуранто останавливает все службы и в конце размонтирует файловую систему.

Для сохранения файлов не с флешки в ПЗУ устройства необходимо выполнить следующую команду: flashfs save && flashfs commit && flashfs enable. Я постоянно забывал сохраняться и после перегрузки набивал изменения снова. Для сохранения файлов в других каталогах необходимо добавить пути в файл /usr/local/.files.

Исполняемым файлам не забывать делать chmod +x file.

SSH

Выполнить:

mkdir -p /usr/local/etc/dropbear
dropbearkey -t dss -f /usr/local/etc/dropbear/dropbear_dss_host_key
dropbearkey -t rsa -f /usr/local/etc/dropbear/dropbear_rsa_host_key

Для автоматического запуска SSH сервера (dropbear) нужно добавить в файл /usr/local/sbin/post-boot следующие строчки:

$log 'Starting dropbear SSH server'
/usr/sbin/dropbear 1>/dev/null 2>&1

В конце:

flashfs save && flashfs commit && flashfs enable

Также необходимо добавить парочку правил в файрвол. Для этого правим /usr/local/sbin/post-firewall:

$log 'SSH rules'
iptables -I INPUT 5 -i $1 -p tcp -d $2 --dport 22 -m state --syn --state NEW -m limit --limit 2/minute --limit-burst 1 -j LOG --log-prefix "SSH Brute force attack: " --log-level 6
iptables -I INPUT 6 -i $1 -p tcp -d $2 --dport 22 -m state --syn --state NEW -m limit --limit 2/minute --limit-burst 1 -j ACCEPT
iptables -I INPUT 7 -i $1 -p tcp -d $2 --dport 22 -m state --syn --state NEW -j DROP

4 Gb Flash

Флешка нужна для установки дополнительного софта, ну, и для хранения файлов, конечно. Также устройство может загружаться с флешки! Но мне это пока не нужно.

Для пробы имелась 512 Mb флешка, на которую ядро сильно ругалось. Пришлось ставить 4 Gb Pretec. Поскольку из самого устройства разбить на разделы не получилось, я использовал Acronis Disk Director 10. Получилось два раздела - Linux swap на 100 с копейками мегабайт и Linux ext3 - остальное.

Через веб морду я отключил встроенный FTP и Samba. В результате не загружались необходимые модули ядра - scsi_mod, sd_mod, usb-storage (как последствие - не выполнялись скрипты pre-mount и post-mount). Их нужно загрузить самостоятельно в /usr/local/sbin/post-boot:

/sbin/insmod scsi_mod
/sbin/insmod sd_mod
/sbin/insmod usb-storage

Что в результате:

  • Ядро пишет: /dev/scsi/host0/bus0/target0/lun0: p2 < p5 p6 >

    p5 (/dev/discs/disc0/part5) - swap, p6 (/dev/discs/disc0/part6) - ext3

  • /dev/discs/disc0/part6 смонтирован как /tmp/harddisk
  • На ext3 создана простейшая структура Unix-каталогов

Установка пакетов

В системе имеется простенький менеджер пакетов - ipkg. По умолчанию весь софт ставится в /opt, поэтому нужно, чтобы в /opt был смонтирован корень ext3. Для этого изменяем /usr/local/sbin/post-mount (вставляем в самое начало после $log 'Begin') :

[ -d /tmp/harddisk/opt ] && $log 'Mounting /opt' && /bin/mount -o bind,sync,noatime,rw /tmp/harddisk /opt

Выполнить (все это описано здесь):

ipkg.sh update
ipkg.sh install ipkg-opt
ipkg update
echo "/etc/ipkg.conf" >> /usr/local/.files
flashfs save && flashfs commit && flashfs enable

Ставим необходимый софт:

ipkg install adduser
ipkg install bash
ipkg install coreutils
ipkg install findutils
ipkg install grep
ipkg install less
ipkg install mc
ipkg install screen
ipkg install util-linux
ipkg install vim
ipkg install which
ipkg install wget-ssl

syslog-ng

syslogd я сразу меняю на syslog-ng. Ставим:

ipkg install syslog-ng
cd /opt/etc
ln -s syslog-ng/syslog-ng.conf syslog-ng.conf

Добавляем в /usr/local/sbin/post-mount:

if [ -x /opt/sbin/syslog-ng ]; then
        $log 'Replacing syslogd'
        [ -x /opt/bin/unlink ] && /opt/bin/unlink /tmp/syslog.log && /opt/bin/ln -s /opt/var/log/all.log /tmp/syslog.log
        kill `/bin/pidof syslogd`
fi

Это, естественно, должно выполнятся до запуска syslog-ng.

\

Не забываем также про остановку системы, файл /usr/local/sbin/pre-shutdown (как можно позже, но перед размонтированием):

if [ -f /var/run/syslog-ng.pid ]; then
        $log 'Terminating syslog-ng'
        /bin/kill `cat /var/run/syslog-ng.pid`
fi

rc.unslung и init.d

Многие пакеты закидывают скрипт запуска службы в /opt/etc/init.d/ с названием SXYService. Нужен механизм, который будет выполнять их в правильном порядке. Создаем исполняемый файл /opt/etc/init.d/rc.unslung с таким содержимым:

#!/bin/sh

# Start all init scripts in /opt/etc/init.d
# executing them in numerical order.
#
for i in /opt/etc/init.d/S??* ;do

    [ ! -f "$i" ] && continue
    [ ! -x "$i" ] && continue

    case "$i" in
       *.sh)
           # Source shell script for speed.
           (
               trap - INT QUIT TSTP
               set start
               . $i
           )
           ;;
       *)
           # No sh extension, so fork subprocess.
           $i start
           ;;
   esac
done

Добавляем вызов /opt/etc/init.d/rc.unslung в конец /usr/local/sbin/post-mount:

[ -x /opt/etc/init.d/rc.unslung ] && $log 'Running rc.unslung' && /opt/etc/init.d/rc.unslung | $log

Запуск syslog-ng из init.d

Теперь происходит автоматически благодаря /opt/etc/init.d/S01syslog-ng.

Настройка syslog-ng

Мой syslog-ng помимо файлов логирует еще и на рабочую машину, где стоит Kiwi Syslog в качестве приемника. Просто привожу конфиг /opt/etc/syslog-ng.conf:

#
# Syslog-ng example configuration for for NSLU2 Unslung 3.x
#
# Copyright (c) 1999 anonymous
# Copyright (c) 1999 Balazs Scheidler
# $Id: syslog-ng.conf 5159 2007-01-12 20:37:44Z fcarolo $
#
# Syslog-ng configuration file, compatible with default Debian syslogd
# installation.
#

options {
        long_hostnames(off);
        sync(0);
        stats(0);
        create_dirs(yes);
        time_reopen(10);
        log_fifo_size(2000);
};

source src {
        pipe("/proc/kmsg" log_prefix("kernel: ") );
        unix-stream("/dev/log");
        internal();
};
#source net { udp(); };

destination authlog { file("/opt/var/log/auth.log"); };
destination syslog { file("/opt/var/log/syslog"); };
destination cron { file("/opt/var/log/cron.log"); };
destination daemon { file("/opt/var/log/daemon.log"); };
destination kern { file("/opt/var/log/kern.log"); };
destination lpr { file("/opt/var/log/lpr.log"); };
destination user { file("/opt/var/log/user.log"); };
destination uucp { file("/opt/var/log/uucp.log"); };
destination ppp { file("/opt/var/log/ppp.log"); };
destination mail { file("/opt/var/log/mail.log"); };

destination mailinfo { file("/opt/var/log/mail.info"); };
destination mailwarn { file("/opt/var/log/mail.warn"); };
destination mailerr { file("/opt/var/log/mail.err"); };

destination newscrit { file("/opt/var/log/news/news.crit"); };
destination newserr { file("/opt/var/log/news/news.err"); };
destination newsnotice { file("/opt/var/log/news/news.notice"); };

destination debug { file("/opt/var/log/debug"); };
destination messages { file("/opt/var/log/messages"); };

destination udp_machinename { udp( your.ip.add.rr port(514) template("$DATE $FULLHOST [$FACILITY.$LEVEL] $MESSAGE\n") template_escape(no) ); };
destination alllog { file("/opt/var/log/all_$R_YEAR-$R_MONTH-$R_DAY.log" template("$DATE $FULLHOST [$FACILITY.$LEVEL] $MESSAGE\n") template_escape(no) ); };

filter f_auth { facility(auth); };
filter f_authpriv { facility(auth, authpriv); };
filter f_syslog { not facility(authpriv, mail); };
filter f_cron { facility(cron); };
filter f_daemon { facility(daemon); };
filter f_kern { facility(kern); };
filter f_lpr { facility(lpr); };
filter f_mail { facility(mail); };
filter f_user { facility(user); };
filter f_uucp { facility(cron); };
filter f_ppp { facility(local2); };
filter f_news { facility(news); };
filter f_debug { not facility(auth, authpriv, news, mail); };
filter f_messages { level(info..warn) and not facility(auth, authpriv, mail, news); };
filter f_emergency { level(emerg); };

filter f_info { level(info); };
filter f_notice { level(notice); };
filter f_warn { level(warn); };
filter f_crit { level(crit); };
filter f_err { level(err); };

log { source(src); filter(f_authpriv); destination(authlog); };
log { source(src); filter(f_syslog); destination(syslog); };
log { source(src); filter(f_cron); destination(cron); };
log { source(src); filter(f_daemon); destination(daemon); };
log { source(src); filter(f_kern); destination(kern); };
log { source(src); filter(f_lpr); destination(lpr); };
log { source(src); filter(f_mail); destination(mail); };
log { source(src); filter(f_user); destination(user); };
log { source(src); filter(f_uucp); destination(uucp); };
log { source(src); filter(f_mail); filter(f_info); destination(mailinfo); };
log { source(src); filter(f_mail); filter(f_warn); destination(mailwarn); };
log { source(src); filter(f_mail); filter(f_err); destination(mailerr); };
log { source(src); filter(f_news); filter(f_crit); destination(newscrit); };
log { source(src); filter(f_news); filter(f_err); destination(newserr); };
log { source(src); filter(f_news); filter(f_notice); destination(newsnotice); };
log { source(src); filter(f_debug); destination(debug); };
log { source(src); filter(f_messages); destination(messages); };
log { source(src); filter(f_ppp); destination(ppp); };

log { source(src); destination(udp_machinename); };
log { source(src); destination(alllog); };

Создание пользователей

Добавляю командой adduser (можно и напрямую править /etc/passwd и /etc/group). Не забываю сделать

echo "/etc/passwd" >> /usr/local/.files
echo "/etc/group" >> /usr/local/.files
flashfs save && flashfs commit && flashfs enable

Домашние папки создаю в /opt/home.

Sun Microsystems bought MySQL

Sun Microsystems Announces Agreement to Acquire MySQL, Developer of the World's Most Popular Open Source Database

Sun Growth Strategy Accelerates With New Position in $15 Billion Database Market SANTA CLARA, CA January 16, 2008 Sun Microsystems, Inc. (NASDAQ: JAVA) today announced it has entered into ... read more

Все, не будет больше J2EE на Оракле работать. MySQL станет круче всех! Гггг

Type system covariance and contravariance

Полагаю, многие разработчики не знают, что такое ковариантность/контравариарность системы типов. К примеру, в версии 1.5 языка Java появилась возможность использовать тип класса-наследника в объявлении замещающей функции:

 0: /**
 1:  *
 2:  */
 3: package com.chabster.covariance;
 4:
 5: /**
 6:  * @author Chabster
 7:  */
 8: public class Container
 9: {
10:   class A
11:   {
12:      A some() {
13:         return (this);
14:      }
15:   }
16:
17:   class B extends A
18:   {
19:      @Override
20:      B some() {
21:         return (this);
22:      }
23:   }
24:
25: }

В С++ такая возможность существует уже давно, а вот в C# все еще отсутствует. Полагаю, это связано с нестыковкой сигнатур методов в C# и IL. Вот, что говорит по этому поводу Standard ECMA-335 Common Language Infrastructure (CLI) 4th edition (June 2006):

A method signatures is composed of

  • a calling convention,
  • the number of generic parameters, if the method is generic,
  • a list of zero or more parameter signatures—one for each parameter of the method—and,
  • a type signature for the result value, if one is produced.

Получается, что IL код различает методы, которые отличаются лишь типом возврата, а C# - нет. Как и любой существующий на данный момент управляемый .NET-язык. С другой стороны этот недостаток можно элементарно восполнить. Вариант реализации предлогаю расценивать, как домашнее задание для читателя :).

В некоторых моментах C#, все же, поддерживает ковариантность и контравариантнось. Но для начала выясним что это такое.

Ковариантность (covariance) и контравариантнось (contravariance)

Ковариантностью называют сохранение формы при преобразовании. Соответсвенно, ковариантным называется преобразование, сохраняющее форму (свойства).

Например, оператор F(x) = x*2 является ковариантным касательно отношения % (делимость). Т.е. из x%y следует, что F(x)%F(y).

Контравариантностью называют обращение формы при преобразовании. Соответсвенно, контравариантным называется преобразование, обращающее форму.

Например, оператор F(x) = -x является контравариантным касательно отношения > (больше). Т.е. из x>y следует, что F(x)<F(y).

Оба этих понятия нельзя правильно обобщить поскольку в каждой науке оно выглядит по-своему. Рассмотрим их в самой интересной для нас области - программирование!

Ковариантность и контравариантнось в системе типов языков программирования

Рассмотрим пример C# кода:

class A {
    A[] aArr = new B[] { };
}

class B : A {
}

Пусть « обозначает отношение is a для типов. Поэтому B«A. Оператор Arrize({T}) = {T[]} является ковариантным касательно отношения is a для ссылочных типов т.к. из B«A следует B[]«A[].

Возвращаясь к первому Java примеру можно сказать, что оператор Methodize({SuperT virtMethod(T1,T2,...)}) = {SubT virtMethod(T1,T2,...)} является ковариантным касательно отношения override между методами.

Ковариантность и контравариантнось в системе типов C#

В C# есть ковариация, связанная с делегатами:

class A {
}

class B : A {
}

delegate A SomeDelegate();

static B SomeMethod() { return (null); }

static SomeDelegate sd = SomeMethod;

Типы возвращаемых значений метода и делегата различны, но совместимы.

В C# есть и контравариация, связанная с делегатами:

class A {
}

class B : A {
}

delegate void SomeDelegate(B b);

static void SomeMethod(A a) { }

static SomeDelegate sd = SomeMethod;

Сигнатуры метода и делегата различны, но совместимы, правда, уже в обратном направлении.

Это все, конечно, хорошо, вот только...

class A {
}

class B : A {
}

delegate T SomeDelegate<T>();

static B SomeMethod() { return (null); }

static SomeDelegate<B> sdB = SomeMethod;

static SomeDelegate<A> sdA = sdB;

... последняя строчка вызывает ошибку компиляции.

В целом, вариантность - очень полезный механизм, если им правильно пользоваться.

.NET Framework Library Source Code

Стали доступны source сервера со следующим содержимым:

  • .NET Base Class Libraries (System, System.CodeDom, System.Collections, System.ComponentModel, System.Diagnostics, System.Drawing, System.Globalization, System.IO, System.Net, System.Reflection, System.Runtime, System.Security, System.Text, System.Threading, etc).
  • ASP.NET (System.Web, System.Web.Extensions)
  • Windows Forms (System.Windows.Forms)
  • Windows Presentation Foundation (System.Windows)
  • ADO.NET and XML (System.Data, System.Xml)

Configuring Visual Studio to Debug .NET Framework Source Code расскажет, как настроить Visual Studio 2008 для вытягивания исходников с этих серверов.

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

HP LaserJet 2015 and WL-500gP

ASUS WL-500g Premium ASUS WL-500g Premium я купил уже давненько, но все еще не "занимался" ним, как я любил заниматься с Линупсами.

HP LaserJet 2015 Сейчас возникла необходимость расшарить этот замечательный принтер HP LaserJet 2015 на два компа.

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

Подключение принтера HP LaserJet 2015 к WL500gP и настройка маршрутизатора

Первое, что необходимо сделать, - подключить принтер через USB к порту рутера. В админке System Setup --> Services разрешить Enable LPR printing и Enable RAW printing. Samba для печати мне нафиг не нужна.

Ядро с радостью сообщает, что увидело принтер:

Jan 16 14:58:38 kernel: hub.c: new USB device 01:03.2-2, assigned address 3
Jan 16 14:58:39 kernel: printer.c: usblp0: USB Bidirectional printer dev 3 if 0 alt 0 proto 2 vid 0x03F0 pid 0x3817
Jan 16 14:58:39 kernel: printer.c: usblp0 Device ID string [170]='MFG:Hewlett-Packard;CMD:PJL,PML,POSTSCRIPT,PCLXL,PCL;MDL:HP LaserJet P2015 Series;CLS:PRINTER;DES:Hewlett-Packard LaserJet P2015 Series;MEM:MEM=23MB;COMMENT:RES=1200x1;'

В админке, правда, Status & Log --> Status не отображает модель и статус принтера. По началу я расстроился, подумал, что ничего не получится, но, все-таки, не остановился на достигнутом. Смотрю netstat -l:

tcp        0      0 *:printer               *:*                     LISTEN
tcp        0      0 *:laserjet              *:*                     LISTEN

Обана! Протокол laserjet, порт 9100! Перехожу к настройке принтера в Windows Vista и XP.

Добавление принтера в Windows Vista и XP

Windows Vista

  1. Иду в 'Control Panel\Printers' и делаю 'Add printer'. Далее 'Add a network, wireless or Bluetooth printer'.

    Choose a local or network printer

  2. Автоматически оно, понятное дело, ничего не нашло, поэтому смело жмем 'The printer that I want isn't listed'.

    Searching for available printers...

  3. Здесь выбираем 'Add a printer using a TCP/IP address or hostname'.

    Find printer by name or TCP/IP address

  4. Вбиваем IP адрес WL500gP.

    Type a printer hostname or IP address

  5. После длительной мозговой деятелности Windows говорит 'Additional port information required' и просит помочь в выборе правильного типа устройства. Нам нужен 'Hewlett Packard Jet Direct'.

    Additional port information required

  6. Наконец просят показать драйвера устройства. Я скачал с офсайта и указал.

    Install the printer driver

  7. Последний шаг - ввод имени принтера.

Как всегда, печатаем тестовую страничку. У меня все окей, а у тебя?

Windows XP

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

Usability этого решения

Пока что проблем не возникало. Буду замечать грабли - обновлю страничку. В первую очередь интересует работа со спулером, очередью печати и поддержка PostScript 2 или 3.

UPDATE: WL-500gP HP 2015 problems.

Coast 2 Coast - Be With Me (Original Mix)

Древние вещи, как правило, трудно отыскать. Трек Coast 2 Coast Feat Amanda Jamison - Be With Me (Original Mix), который срывает мне башню, я искал пару месяцев. И наконец свершилось чудо - я нашел сайт на котором был живой e2k линк. MP3 download link, на всякий случай...

Best subtitle editor

Jubler logo Jubler subtitle editor пока что лучшая утилита для редактирования, конвертации или создания файлов субтитров. Интерфейс несколько неудобен (Java), но альтернатив я не нашел.

Заодно выкладываю парочку субтитров, с поиском которых возникли проблемы.

NewsGator FeedDemon became free

Free beer Мой Best RSS Reader - NewsGator FeedDaemon. И он теперь стал бесплатным!

Также в скором времени бесплатными станут NetNewsWire, NewsGator Inbox и NewsGator Go.

Visual C++ 2008 Feature Pack Beta

Вышла Visual C++ 2008 Feature Pack Beta вместе с Visual C++ 2008 Feature Pack Beta Documentation.

Размер апдейта несколько смущает - 303.51М. В составе:

  1. Standard C++ Library TR1 Extensions

    Microsoft's implementation includes support for fixed size arrays, function objects, hash tables, regular expressions, template metaprogramming, template types for aggregating related objects, and random number generation.

    Some features of TR1, such as the mathematical functions and certain C99 additions, are not included in the Visual C++ implementation of TR1.

  2. MFC Feature Pack for Visual C++ 2008

    MSOffice2007Demo

This Feature Pack Beta release may fail to install if you do not have a complete installation of Visual Studio 2008 on your system. To workaround this issue, ensure your original Visual Studio 2008 installation source (network path or DVD) is available when installing the Feature Pack.

Напомню, что MFC стала expanded за счет библиотеки BCGControlBar Pro и при беглом взгляде она хорошо ощущается - внешний вид, функционал, дизайн, производительность, переделанные примеры. Классы переименованы в CMFCXyz.

Примеры, кстати, находятся в каталоге C:\Program Files\Microsoft Visual Studio 9.0\Samples\1033\AllVCLanguageSamples.zip\C++\MFC\Visual C++ 2008 Feature Pack.

На блоге Visual C++ Team Blog есть интересный пост Q&A on our TR1 implementation о Feature Pack TR1.

Google KAK

Firefox 2. Похоже, проблемы целлюлита, месячных и плохих зубов уже не актуальны. Google знает!

как заткнуть эту ебучую телегу

  • как завязать галстук
  • как разрушить свой моск
  • как похудеть
  • как встречать 2008 год
  • как убивали спартак
  • как правильно целоваться

Run as interactive user from service

The code below runs a program on interactive desktop with logged on user privileges, as it was started by user himself. Must be executed by Local System, for example, by Windows service.

stdafx.h:

#include <WtsApi32.h>
#pragma comment(lib, "WtsApi32.lib")

#include <Userenv.h>
#pragma comment(lib, "Userenv.lib")

RunAsInteractiveUser function:

BOOL bRet;
HRESULT hr;

HANDLE processToken = NULL;
TOKEN_PRIVILEGES oldTokenPrivileges = { 0 };

HANDLE impersonationToken = NULL;
HANDLE userToken = NULL;

LPVOID pEnvironment = NULL;
PROCESS_INFORMATION processInformation = { 0 };

__try {
    bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken);
    if (!bRet) {
        hr = GetLastError();
        return hr;
    }

    // This step might not be necessary because SeTcbPrivilege is enabled by default for Local System
    LUID luid;
    bRet = LookupPrivilegeValue(NULL, _T("SeTcbPrivilege"), &luid);
    if (!bRet) {
        hr = GetLastError();
        return hr;
    }

    TOKEN_PRIVILEGES adjTokenPrivileges = { 0 };
    adjTokenPrivileges.PrivilegeCount = 1;
    adjTokenPrivileges.Privileges[0].Luid = luid;
    adjTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    DWORD dwOldTPLen;
    bRet = AdjustTokenPrivileges(processToken, FALSE, &adjTokenPrivileges, sizeof(TOKEN_PRIVILEGES), &oldTokenPrivileges, &dwOldTPLen);
    if (bRet) {
        hr = GetLastError();
        if (hr == ERROR_SUCCESS);
        else if (hr == ERROR_NOT_ALL_ASSIGNED) {
            // Enabled by default
        }
    }
    else {
        hr = GetLastError();
        return hr;
    }

    DWORD conSessId = WTSGetActiveConsoleSessionId();
    if (conSessId == 0xFFFFFFFF) {
        // There is no session attached to the console
        return ERROR_SUCCESS;
    }

    bRet = WTSQueryUserToken(conSessId, &impersonationToken);
    if (!bRet) {
        hr = GetLastError();
        return hr;
    }

    bRet = DuplicateTokenEx(impersonationToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &userToken);
    if (!bRet) {
        hr = GetLastError();
        return hr;
    }

    STARTUPINFO si = { 0 };
    si.cb = sizeof(STARTUPINFO);
    si.lpDesktop = _T("winsta0\\default");

    bRet = CreateEnvironmentBlock(&pEnvironment, userToken, TRUE);
    if (!bRet) {
        hr = GetLastError();
        return hr;
    }

    bRet = CreateProcessAsUser(userToken, _T("C:\\Windows\\notepad.exe"), NULL, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, NULL, &si, &processInformation);
    if (!bRet) {
        hr = GetLastError();
        return hr;
    }
}
__finally {
    if (processInformation.hThread) {
        CloseHandle(processInformation.hThread);
    }
    if (processInformation.hProcess) {
        CloseHandle(processInformation.hProcess);
    }
    if (pEnvironment) {
        bRet = DestroyEnvironmentBlock(pEnvironment);
    }
    if (userToken) {
        CloseHandle(userToken);
    }
    if (impersonationToken) {
        CloseHandle(impersonationToken);
    }
    if (processToken) {
        bRet = AdjustTokenPrivileges(processToken, FALSE, &oldTokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
        CloseHandle(processToken);
    }
}

Windows Vista Security Model Analysis

Symantec, как всегда, на высоте. Документ Windows Vista Security Model Analysis ооочень полезен для разработчиков, которые занимаются низкоуровневым и/или WinAPI программированием.

Документ покрывает темы:

  • Computer security
  • Windows Vista
  • Windows Resource Protection
  • File Virtualization
  • Registry Virtualization
  • Interrity Level
  • UAP
  • LUA
  • UIPI

Weak Static and Strong Dynamic typing

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

Типизация языков программирования - одна из немногих основополагающих концепций, которыми языки отличаются. Она влияет на все составляющие части - от семантики до компиляторов и средств разработки.

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

О типах и типизации

Прежде чем разговаривать о типизации и ее видах необходимо разобраться с самими понятиями типа и типизации.

Тип - это некая информация о участке памяти.

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

Как ни крути, а екземпляр любого типа T представляет собой кусок памяти размером sizeof(T) или что-то в этом духе. Задача компилятора состоит в трансляции конструкций языка с определенной семантикой в последовательность инструкций процессора.

Типизацией назовем совокупность правил, которыми обязан руководствоваться компилятор при трансляции операций соответствующего языка над типами данных в машинный код.

Строгая (strong) и нестрогая (weak) типизация

Мне кажется, что эти два противоположных понятия следует объединить в одно - строгость типизации. Рассмотрим похожие фрагменты кода различных языков:

// PHP - говорят, что это язык с нестрогой типизацией
$a = "1";
$b =  2;
$c = a + b;
// $c == 3
// C# 3.0 - говорят, что это язык со строгой типизацией
String a = "1";
Int32 b =  2;
var c = a + b;
// $c == "12"

Что же я вижу здесь? Первое: переменные могут быть связаны с типом данных (C# 3.0) или не связаны (PHP). Это влияет на возможность определить тип результата или легитимность операций во время обработки исходного кода (компилятором или средой разработки). Многие утверждают, что именно это указывает на строгую или нестрогую типизацию, а я считаю, что привязанность переменных к типам языка напрямую связана с разделением на статическую и динамическую типизацию. Второе: компилятор имеет некие правила преобразования конструкций, которые вызывают нестыковку с системой типов языка или представлением разработчика о их логичности. Чем больше этих правил - тем меньше строгость типизации. Насколько мне известно, все языки имеют поддержку минимум одного неявного преобразования типов (coercion), поэтому языка с 100%-но строгой типизацией не существует.

Статическая (static) и динамическая (dynamic) типизация

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

Статическая типизация (static typing)

Рассмотрим пример на языке С++:

class CppClass
{
public:
    void SomeOperation() {}
};

void PerformOperation(CppClass &c) {
    c.SomeOperation();
}

...

CppClass c;
PerformOperation(c);

Компилятор С++ знает все, что ему требуется для трансляции этого кода в машинные инструкции - адрес переменной c, ее тип - CppClass, наличие метода SomeOperation и его точный адрес в памяти. Информация о типах используется компилятором и, как правило, не добавляется в выходной модуль.

Статическая типизация не запрещает компилятору встраивать информацию о типах:

// C++ с включенной поддержкой RTTI
DerivedCppClass derived;
BaseCppClass &base = derived;

...

DerivedCppClass &derived = dynamic_cast<DerivedCppClass&>(base);

Пользы от RTTI в С++ практически нет, поскольку единственное, что можно узнать о типе во время выполнения, - это его строковое имя, а наличие в коде операторов dynamic_cast свидетельствует о плохом понимании программистом принципов ООП.

В управляемых языках, таких как Java или C#, подробная информация о типе зашивается в выходной модуль и доступна для использования во время выполнения программы. Этот механизм называется интроспекцией (reflection) и имеет свою довольно высокую цену в виде многократной потери производительности.

Динамическая типизация (dynamic typing)

Python - хороший пример динамически типизированного языка:

class C(object):
   def hello():
      print 'Hello!'

def x(c):
   c.hello # WTF?!

c = C()
x(c)

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

Преимущества и недостатки статической (static) и динамической (dynamic) типизаций

Статическая (static) типизация

Преимущества:

  • Легче ориентироваться в коде
  • Хорошая поддержка со стороны средств разработки (рефакторинг, подсказки)
  • Скорость выполнения
  • Скорость выполнения
  • Скорость выполнения

Недостатки:

  • Усложнение языка (и, как последствие, увеличение стоимости разработки и поддержки)
  • Реализация полиморфизма через наследование
  • Ложное впечатление о корректности кода (компилируется - значит работает)
  • И еще

Динамическая (dynamic) типизация

Преимущества:

  • Хорошая поддержка современных стандартов и технологий (SOA, например)
  • Выразительность и простота кода
  • Скорость разработки
  • И еще

Недостатки:

  • Низкая производительность
  • Необходимость качественного и обширного тестирования

Дополнительные ссылки по теме:

Windows Server 2008 SDK and .NET 3.5

Прямой линк без всяких валидаций: Microsoft® Windows® Software Development Kit (SDK) for Windows Server® 2008 and .NET Framework 3.5!

Canadian Mind Products

Тяжело сказать что такое Canadian Mind Products. Энциклопедия что-ли?

Зато вот о Roedy Green’s Java & Internet Glossary можно сказать лишь одно - просто замечательнейший reference/documentation по Java. Пользуюсь часто и всем советую. Почти каждая страница содержит описание книжек по теме.

Visual Studio debugging tips

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

Довольно часто приходится вставлять куски кода вида hr = GetLastError();, запускать Error Lookup чтобы прочитать человеческим языком что же произошло. Я уже давно пользуюсь одним из более удобных способов:

  1. Если статический тип переменной - HRESULT, то окошки дебаггера Visual Studio (Watch #, Autos, Locals etc) автоматически отображают в колонке Value строку ошибки вместо кода.
  2. Псевдопеременная err или @err отображает текущее числовое значение GetLastError(). А для отображения строки вместо числа нужно применить форматирование - добавить через запятую hr. Пример: @err,hr.

    Visual Studio поддерживает множество форматов. Подробнее - Visual Studio Debugger --> Format Specifiers in C++

  3. При вызове COM-метода возвращаемое значение HRESULT попадает в регистр eax, поэтому @eax,hr расскажет нам в чем беда.

Есть и другие псевдопеременные - $handles, $vframe, $TID, $ENV, $CMDLINE, @registername или $registername, $clk, $user. Приятно, а?

JVM Hosting

Хостинг усправляемого кода в программных продуктах уже не редкость. Oracle поддерживает выполнение байткода JVM еще с 8-й версии и периодически выполняет апгрейд виртуальной машины (11g поставляется с версией 1.5). Сравнительно недавно добавили поддержку .NET 1.1 и .NET 2.0. Microsoft SQL Server 2005 имеет поддержку CLR 2.0 и, кстати, внутренняя архитектура последнего была усовершенствована (скорее заточена) под задачи хостинга. Дальше я расскажу, как добавить возможность выполнять JVM-код в приложение, написанное на С++.

Подготовка проекта для хостинга виртуальной машины Java.

Первое, что необходимо сделать, - поставить Java Development Kit версии 6. Далее в настройках проекта прописать папку поиска заголовочных файлов JDK - "C:\Program Files\Java\jdk1.6.0_03\include\", папку с библиотечными файлами - "C:\Program Files\Java\jdk1.6.0_03\lib" и добавить путь поиска jvm.dll - C:\Program Files\Java\jdk1.6.0_03\jre\bin\client\ как написано здесь. Еще нужно закинуть файл "c:\Program Files\Java\jdk1.6.0_03\include\win32\jni_md.h" в папку с проектом и включить его в проект. Традиционно изменяем stdafx.h:

#include <jni.h>
#pragma comment(lib, "jvm.lib")

Инициализация виртуальной машины Java.

Перед использованием, виртуальную машину Java необходимо загрузить и инициализировать. По-сути, она является песочницей (sandbox) для выполняемого байт-кода, другими словами, мини-операционной системой в пределах хост-процесса. Инициализация предполагает создание управляющих структур, выделение пулов памяти и других ресурсов ОС, связанных с конкретным екземпляром JVM (да, их можно создать несколько).

jint jRet;

JavaVMOption options[1];
options[0].optionString = "-verbose:class,gc,jni";

JavaVMInitArgs jvmInitArgs;
jvmInitArgs.version = JNI_VERSION_1_6;
jvmInitArgs.nOptions = _countof(options);
jvmInitArgs.options = options;
jvmInitArgs.ignoreUnrecognized = JNI_TRUE;

JavaVM *pJvm;
JNIEnv *pEnv;
jRet = JNI_CreateJavaVM(&pJvm, reinterpret_cast<LPVOID*>(&pEnv), &jvmInitArgs);
if (jRet != JNI_OK) {
   // Это залет, солдат!
   cout << "JNI_CreateJavaVM returned " << jRet << endl;
   return(jRet);
}

Структура JavaVMInitArgs содержит настройки создания екземпляра JVM - запрашиваемую версию и различные конфигурационные параметры. Функция JNI_CreateJavaVM, как нетрудно догадаться из ее названия, создает виртуальную машину и возвращает указатель на управляющий интерфейс - JNIEnv. Любые дальнейшие манипуляции выполняются посредством него.

Выполнение байт-кода JVM.

Не буду оригинален. Вашему вниманию представляется вариант "Hello World" в исполнении C++/Java:

void printHelloWorld(JNIEnv *pEnv) {
   jclass classSystem = pEnv->FindClass("java/lang/System");
   jfieldID fieldId = pEnv->GetStaticFieldID(classSystem, "out", "Ljava/io/PrintStream;");
   jobject system_out = pEnv->GetStaticObjectField(classSystem, fieldId);

   jclass classPrintStream = pEnv->FindClass("java/io/PrintStream");
   jmethodID methodId = pEnv->GetMethodID(classPrintStream, "println", "(Ljava/lang/String;)V");

   jvalue v;
   v.l = pEnv->NewStringUTF("Hello World!");
   pEnv->CallVoidMethodA(system_out, methodId, &v);
   if (jthrowable ex = pEnv->ExceptionOccurred()) {
      pEnv->ExceptionDescribe();
      pEnv->ExceptionClear();
   }
}

Завершение работы.

По завершению работы с JVM рантайм нужно удалить:

jRet = pJvm->DestroyJavaVM();
if (jRet != JNI_OK) {
   cout << "pJvm->DestroyJavaVM() returned " << jRet << endl;
   return(jRet);
}

Google new autocomplete

Google new autocomplete
Copyright 2007-2011 Chabster