суббота, апреля 28, 2007

Мичуринцы

Не растут в российских гаражах стартапы. Хоть ты тресни. То ли почва плохая, то ли климат не тот, зимой в российских гаражах, кстати, очень холодно, в отличии от Bay Area. Оффшоры - растут, не в гаражах правда, а стартапы ни в какую. А всем хочется не оффшоров, а стартапов. А они не растут.
И что самое обидное, свалит кто нибудь из наших в Калифорнию. Смотришь, сразу там гараж нашел и стартап свой в нем заорганизовал.
Вот и решили добрые люди культивировать стартапы целенаправленно, районировать и селекционировать, так сказать. Создали для этого специальную программу Start in Garage. Рекламу этого дела пустили по рунету, анонсы проскочили во многих блогах (вот, Михаил Елашкин об этом писал). Оно понятно, всем хочется стартапов.
Программа нешуточная, четыре дня специально обученные люди будут рассказывать и показывать о том, как с нуля (в гараже) поднять стартап. И не только в регионе Садового кольца. Специально обученные люди поедут в глубинку, в Нижний, в Казань, Томск, далее - везде.
Стоит, правда, это почти $900 :(
Для Томска, например, это я вам скажу, деньжищи.
Продать свой гараж, чтоль, да на вырученные деньжата поехать послушать специально обученных людей? Только где же я потом свой стартап стартовать буду?


P.S. Настроение - пятничное, несмотря на субботу :)

пятница, апреля 27, 2007

Windows Update - страшная сила

Я опять про Windows Update. Запустили у нас централизовано эту службу на всех компьютерах. Ох и намаялись мы с ней...
Есть у нас проект на ASP.Net 1.1, а в том проекте полторы дюжины отчетов на Crystal Reports for Visual Studio 2003, версия у них древняя - 9.5, но нам хватает. Не так давно, прямо перед выходом Win2003 SP2, в одно прекрасное утро тестировщики обнаружили, что все отчеты выдаваемые в формате Excel перестали работать на одном тестовом сервере. Мы решили, что очередной билд получился битый и начали разбираться с билдом, инсталятором, отчетами и т.д. На следующий день та же участь постигла и другой тестовый сервер, причем никакого нового билда туда не выкладывали. Просто утром престали работать отчеты, и все тут тебе. После двух дней разбирательств выяснилось, что причина в одном из автоматических апдейтов, который вступает в силу только после перезагрузки сервера. И этот апдейт вошел в состав Win2003 SP2 :(. Ошибка уже всплыла в Интернете, но надеяться на скорый патч древней версии Crystal Reports особо не приходилось.
А теперь главное. Система наша уже давно стоит под нагрузкой и пользуются ею около 3000 пользователей. И на сервере том тоже включен Windows Update!!! Час Х мог наступить в любой момент :))
С большим трудом удалось нам уговорить IT службу сделать "исключение" из правил и выключить эту службу на production сервере.
Мораль сей басни такова - никогда не включайте "Automatic Windows Update" на рабочих (production) серверах.

Многозадачность и многопоточность в Windows

Очень хорошая статья "То, что вам никто не говорил о многозадачности в Windows". Автор Роман Лут.
Статья будет интересна не только игроделам. Написана живым и ясным языком, без закидонов и километров исходников. Если вы хотите знать на что-же все таки влияет приоритет потоков, что происходит при вызове Thread.Sleep(), и почему много потоков это почти всегда хуже, чем один поток - welcome.

среда, апреля 25, 2007

Не ужились...

Я уже писал, что благодаря Windows Updates в моем рабочем компьютере объявился .Net Framework 3.0 и в том числе Windows Cardspace. И вот сегодня, таки я собрался пощупать эту штуковину своими руками. При попытке запустить оснастку CardSpace из панели управления, десктоп компьютера подмигнул мне серым цветом и CardSpace исчез, в смысле упал. Опаньки, подумал я, и полез в EventLog Viewer, где обнаружил такое вот сообщение об ошибке:
The Windows CardSpace service is too busy to process this request. User has too many outstanding requests.

Саму же "чрезвычайно занятую" службу CardSpace я обнаружил при помощи Task Manager, болтающуюся в памяти в совершенно беспомощном состоянии в виде процесса infocard.exe. Добив несчастную я скопировал сообщение об ошибке и отправился гуглить свою проблему. Каково же было мое изумление, когда оказалось, что убийцей последнего писка технологической моды в области безопасности оказался мой любимый, маленький, белый и пушистый месенджер Miranda IM.
Оказалось что это давно уже не новость Sergey Shishkin сообщал об этом еще 22 сентября прошлого года. Однако воз и ныне там. Миранда говорит, что она тут не при чем, что Миранда "is a plain Win32 program and doesn't deal with .NET in any way." Правда, говорят если в Миранде отключить опцию "Options/Status/Idle/Become idle if computer is locked", то все будет работать. Это наталкивает на мысль о бесчетном множестве потенциальных конфликтов CardSpace с разнообразным софтом наподобие скринсейверов и т.п.
В общем, ничего страшного, если бы это была бета. Но CardSpace пришел ко мне через Windows Update и это не бета.

Ответ настоящего джедая...

Диалог на форуме RSDN:
N>>>Как лучше передавать значения (и хранить их) в классе коллекции — по ссылке или по значению. По-моему — это палка о 2-х концах — если хранить и возвращать по указателю (или ссылке) теряется контроль, ведь значние могут и удалить (delete) без ведома класса-коллекции. А если по значению — то если значение — это большая структура или класс — то это большие расходы времени на засылку в стек и извлечение и памяти в стеке. Так кто что посоветует?

L>> C# :)

N> Смешно. Да и идея в принципе неплохая. Один недостаток — не нравится мне очередная поделка мелкомягких. Все слишком просто — расслабляет.


P.S. Курсив мой :)

воскресенье, апреля 22, 2007

Строки в C#

Работа со строками всегда специфична для разных языков программирования. Не обошлось без своей специфики и в C#. Тема вобщем-то не новая и уже обсуждалась много раз, но намедни я столкнулся с ней вновь.
Пришлось мне разбираться с одним багом: web сервис в структурах выходных данных местами выдавал пустые строки. Разбирательства вывели меня на вот этот метод:


protected static void TryToFill(string target, string source)
{
if (source != null && source.Length > 0)
{
target = source;
}
}


Как вы думаете, что выдаст на консоль вот такой код:


string s = "source";
string t = "";
TryToFill(t, s);
Console.WriteLine("target = '{0}'",t);


На выходе получаем:


target = ‘’


Метод TryToFill был предназначен для заполнения строки target данными из строки source. Но он не работает. Строка target после его вызова остается пустой
Строки в C# - ссылочные типы, память под них выделяется в куче. Передача в метод ссылочного типа в качестве параметра означает передачу ссылки на объект. Все изменения с параметром ссылочного типа внутри метода будут видны снаружи этого метода. Можно проверить. Массив - типичный ссылочный тип в C# - вот с ним и поэксперементируем:


public static void Change(byte[] val)
{
val[0] +=1;
}


Теперь создаем массив, передаем его в метод и смотрим его состояние до и после.


byte[] b = new byte[]{1,2};
Console.WriteLine("b[0] before Change() is '{0}'", b[0]);
Change(b);
Console.WriteLine("b[0] after Change() is '{0}'", b[0]);


Получаем:


b[0] before Change() is '1'
b[0] after Change() is '2'


Что и требовалось доказать. Но почему такой подход не работает со строками?
Все это из-за того, что в .Net строки сделаны immutable. Это означает, что однажды созданный объект больше никогда не меняет своего состояния. Для этого класс String сделан sealed, у него переопределены операции сравнения, доступ к символам строки возможен только на чтение, а при присваивании значения строке компилятор шаманит и создает новый экземпляр строки.
Что происходит в злополучном методе TryToFill? После вызова метода параметр target ссылается туда же, куда и переменная t. Но при присваиваниии target = source, создается копия значения source, на которую теперь ссылается target, а значение пременной t остается неизменным.
Для чего все это нужно?
Вот что говорит на эту тему Brian Harry в "Conversations on .NET":
Q: In reference to string manipulation, it appears that we can no longer directly manipulate the buffer in managed code. For example, string[0] = 'a'. Instead we do something like string.Replace(0, 'a') and get a new string back.

Brian Harry: Yes, the reason for this is that strings are immutable. This makes for a simpler programming model and eliminates problems around synchronization and sharing of strings. In some cases, it allows us to give better performance by avoiding copying strings.

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

пятница, апреля 20, 2007

Зачем нужен .Net Framework

"Hello everyone, I am the Lead Architect of the CLR/UIFX group and the Chief Architect of the .NET Frameworks."

Так начинается первый пост в блоге Патрика Дассуда . В блоге всего три сообщения, после которых Патрик видимо решил, что корпоративный план по блоггингу он выполнил. Хотя мне кажется, достаточно было бы одного Рождение CLR
Многие спорят, хотела ли Microsoft создавая .Net скопировать или улучшить Java, или просто искала замену для COM. Оказывается не то и не другое:
"...we cared about - support for other languages, deep interop with COM and unmanaged code, extensive frameworks to expose MS technology "

Оказывается на перврм месте стояла возможность глубокого взаимодействия с нативным кодом и COM. В следующем посте Патрик еще раз подтверждает это, подчеркивая что эта проблема имела не только технологический но и политический характер. Стоит вспомнить о том, что именно попытки Microsoft "приспособить" свою реализацию JVM к более тесному взаимодействию с Windows и стали тем самым камнем преткновения в отношениях Sun и Microsoft, который породил и знаменитые судебные разбирательства и, вероятно, послужил причиной создания .Net.
Ну и конечно, настоящий брилиант, это рассказ Патрика Дассуда о том как он наборсал сборщик мусора для CLR на Common Lisp, а затем сделал транслятор, чтобы перевести его на С++ :)

четверг, апреля 19, 2007

Трудности параллелизма

По наводке Алены С++ прочитал интервью Джо Даффи в блоге Thinking Parallel из серии Interviewing the Parallel Programming Idols. Джо Даффи был program manager группы разработки .Net Threads в CLR team. В своем интервью он рассказывает о том как трудно пробиваться росткам параллелизма через врожденные пороки Windows и .Net :) В общем довольно пессимистичный взгляд на многопоточность в .Net.
Меня же больше удивило другое:
" ... Joe Duffy, who used to be the concurrency program manager on the Common Language Runtime team from Microsoft. He is now back to being a developer and works on parallel libraries, infrastructure, and programming models in Microsoft’s Developer Division."

Вы видели у нас program manager-ов которые все бросили и вернулись назад к программированию?

P.S. Здесь перевод на русский и довольно забавная дискуссия уже успела развернуться :)

среда, апреля 18, 2007

MSDN magazine - май

Вышел майский номер MSDN magazine.
Джувел Лоуи (Juval Lowy) продолжает публикации на тему WCF. На этот раз Распространение транзакций WCF. Похоже, книга, которую пишет Джувел на тему WCF, потихоньку появится в MSDN Magazine мелкими порциями.
Очень интересная статья "9 параллельных структур данных и алгоритмов для многократного использования". Наконец то появилось что-то интересное на тему реализации неблокирующих алгоритмов на .NET. В статье рассморены именно алгоритмы и структуры данных для паралельных вычислений, а не способы использования классов пространства имен System.Threading. У меня давно вызревает идея попытаться создать библиотеку подобную java.util.concurrent.atomic с примитивами для неблокирующих алгоритмов, может эта статья подтолкнет меня на этот шаг. Как говорится, решительный шаг вперед - это обычно результат хорошего пинка под зад.

По ту сторону .Net Framework

Просто удивительно, что я наткнулся на эту ссылку только сейчас. Microsoft Win32 to Microsoft .NET Framework API Map - это по сути полное описание того, как .Net Framework взаимодействует с ресурсами операционной системы.
Например, оказывается что все три типа таймеров в .Net, несмотря на их различия, завязаны на одну системную функцию SetTimer, а во различные наследники класса System.Threading.WaitHandle используют разные механизмы реализации. И т.д., и т.п.
В общем, однозначно - в закладки.

суббота, апреля 14, 2007

Усталость проекта.

Проекты разработки ПО имеют неприятную тенденцию разваливаться со временем. Тут уж ничего не поделаешь – тенденция объективная. Не даром для обозначения этой тенденции используют термин «энтропия» - необратимое и неодолимое рассеяние энергии. Чем дольше длится проект, тем сложнее поддерживать его в тонусе. В проекте накапливается усталость. Это касается как психологических аспектов (усталость команды), так и технических (возрастает доля времени, затрачиваемого на рефакторинг и устранение ошибок), и наконец просто системных - возрастает сложность системы.
Хорошо если проект успевает закончиться до того, как его усталость достигнет критического уровня (не важно с каким результатом). А если нет, если проект достаточно продолжительный? Чаще всего, накопив критическую усталость, проект разваливается. Иногда он рушится с громом и пылью, погребая под своими обломками менеджера, а команда стремительно разбегается, куда глаза глядят. Но гораздо чаще наступает тихая смерть. Проект вроде бы продолжает существовать, но уже как зомби. Команда отчаянно сражается с проблемами, которые растут как снежный ком, но становится все хуже и хуже. Все начинают тихо ненавидеть друг друга, никто уже не понимает, как работает «эта чертова система» («неужели это все мы написали!»). Я не хочу расписывать то, что поддерживает на плаву проекты–зомби, потому что существует и третий сценарий.
Третий сценарий - это когда команде удается найти такие механизмы, позволяющие успешно противостоять проектной энтропии. Такие случаи есть, и когда это случается проект переходит в «промышленный режим» разработки, в котором он может жить годами. Команда продолжает клепать релиз за релизом, без революций и переворотов. Это продолжается, даже если из команды уходят герои одиночки, для которых стиль работы - “mission impossible”.
Как же пережить «критические дни»? По большому счету любая методология, будь то RUP, Agile или XP направлена именно на то, чтобы противостоять энтропии проекта. Но проекты с одинаковым успехом разваливаются под любой методологией (правда, проекты без всякой методологии, все же разваливаются быстрее всех). Но все-таки есть средства, которые действуют против усталости проекта наиболее эффективно. Некоторые из них до смешного просты.
Например, ежедневные короткие митинги (совещания) проектной команды. Эта практика есть в XP и Scrum. Я видел проекты на RUP и MSF, где она успешно прижилась. Надо только придерживаться нескольких простых правил. Митинг должен быть действительно коротким, не более 30 минут. Должен быть ведущий, не обязательно это должен быть менеджер проекта, но кто-то должен следить за соблюдением правил. Каждый должен отчитаться о том, что он сделал вчера. Любая возникшая вчера проблема должна быть обсуждена. Каждый должен получить задание на сегодня (тут зависит от методологии, например, в Agile и XP разработчики сами выбирают себе задачи). Такие митинги очень хорошо поддерживают тонус проекта. Я не знаю точно почему, вероятно дело в психологии. Каждый разработчик знает, что завтра ему надо отчитаться перед командой. С другой стороны он всегда уверен, что не никогда останется один на один внезапно возникшей проблемой. Ежедневные митинги - это пульс проекта.
Постоянная интеграция – это вторая непреходящая ценность, почитаемая адептами всех религий методологий без исключения. Интеграция включает в себя сборку новой версии системы и развертывание ее на тестовом окружении. Но с этой практикой сложнее, чем с предыдущей. Дело в том, что со временем сложность разрабатываемой системы растет, в интеграцию включаются все новые аспекты и соответственно усложняется процесс интеграции. Он требует все больших и больших затрат, а поэтому возникает соблазн проводить интеграцию все реже и реже. Редкие интеграции приводят к тому, что между ними накапливается много изменений, и их становится труднее интегрировать в систему. Возникает порочный круг. Если вы хотите удержать проект от развала вы должны во чтобы-то ни стало поддерживать частую или непрерывную интеграцию. MSF и XP рекомендуют проводить интеграции не реже одного раза в день. Не имеет значения выполняете вы интеграцию в ручную или автоматически. Гораздо важнее то, что процедура интеграции должна быть ясной, четкой и по возможности простой. Плохо, когда знание о процедуре интеграции становится «сакральным» и замыкается на одном члене команды (если, конечно, он не выделенный конфигурационный инженер). Хорошо если интеграцию умеет выполнять любой член команды (как того требует XP).
Модульные тесты (unit tests) я, пожалуй, поставлю на третье место. Если в вашем проекте третью неделю на сборке не проходят модульные тесты, или их вообще выкинули из сборочного скрипта – значит ваш проект начинает разваливаться. Если модульных тестов у вас не было с самого начала – проект, вероятно, не стоило начинать. По моим наблюдениям, после полугода у половины проектов модульные тесты начинают, что называется, хромать. Причины этого - возрастающая сложность и обыкновенная лень. Чем дольше система, тем сложнее писать тесты. Чтобы протестировать класс бизнес логики надо подготовить тестовые данные в БД или написать целую кучу заглушек и мок объектов. Когда объем тестов начинает превышать объем основного кода – это начинает напрягать практически любого программиста. Наконец наступает момент, когда лень берет верх над здравым смыслом, сначала перестают писать новые тесты, потом - поддерживать уже написанные. Все, после этого остается одна надежда на тестировщиков, и на то что, в очередном билде они проведут полное регрессивное тестирование и ничего не пропустят.
Четвертым номером идет дизайн. Грамотно спроектированная система намного лучше противостоит проектной энтропии. Недостаточное внимание к дизайну, слабая сторона многих методологий. Я говорю здесь не об ошибках в проектировании и архитектуре, а именно об отсутствии элементарных общих подходов к построению приложения. Представьте себе систему, в которой настройки хранятся в трех разных местах, а чтение этих настроек выполняется четырьмя разными способами. А если то же самое творится с доступом к данным и бизнес логикой? А если эта система уже в эксплуатации и вам приходится постоянно вносить в нее изменения? Это сущая каторга. А начиналось как обычно все красиво, с энтузиазмом, в стиле XP или чего-то еще в этом роде... В общем, не стоит пренебрегать дизайном.
Бороться с усталостью проекта можно и нужно. И не важно, по какой методологии разрабатывается ваш проект, поддерживайте его тонус, и будет вам удача.

пятница, апреля 13, 2007

А я не знал...

Сборку .Net 1.1 можно загрузить в приложение .Net 2.0. Прочитал об этом у Gaidar Magdanurov вот здесь. Там и пример кода есть.
Не знаю почему, но мне казалось, что это не возможно.
"Век живи, век учись - дураком помрешь" (с) народная мудрость.

четверг, апреля 12, 2007

Blogger: Теперь по русски

Сегодня с утра с удивлением обнаружил, что интерфейс Блогера стал русским. А вот и официальное сообщение Официальный блог - Google Россия: Blogger: Теперь еще больше языков
Оказывается добавлена поддержка восьми языков. Восьмой - ภาษาไทย (что это такое знают только ภาษาไทยцы :)
Есть кстати и баги. На странице редактирования макета, на элементах макета ссылочка "Изменить" превращается в "Edit" после сохранения изменений. Но это мелочи.
Спасибо Blogger.

Охота за исключениями

(с иллюстрациями и поеданием выловленных трофеев)


Недавно встретил на удивление бессмысленный код:


try
{
//что то там делаем
}
catch
{
throw;
}


Эта конструкция try - catch не делает ровным счетом ничего. Я засомневался и решил проверить, для чего написал такой код:


class Class1
{
[STAThread]
static void Main(string[] args)
{
try
{
CatchException();
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadLine();
}
// здесь будет exception
public static void RiseException()
{
throw new MyException("Deep exeption");
}
// это просто обертка
public static void Wrap()
{
RiseException();
}
// здесь ловим исключение
public static void CatchException()
{
try
{
Wrap();
}
catch
{
throw;
}
}
}

// это наше собственное исключение
public class MyException : ApplicationException
{
public MyException(string message):base(message){}
}


В методе RiseException мы генерируем наше собственное исключение MyException. В методе CatchException мы воспроизводим эту странную конструкцию try-catch, а в методе Main наблюдаем что из этого всего вышло.
Итак на выходе данной программы мы имеем:


Catch.MyException: Deep exeption
at Catch.Class1.RiseException() in c:\temp\solutions\catch\class1.cs:line 24
at Catch.Class1.Wrap() in c:\temp\solutions\catch\class1.cs:line 30
at Catch.Class1.CatchException() in c:\temp\solutions\catch\class1.cs:line 42

at Catch.Class1.Main(String[] args) in c:\temp\solutions\catch\class1.cs:line
12


Что мы видим? Информация о типе исключения сохранилась. Сообщение на месте. Информация о меcте возникновения исключения и стеке вызовов тоже есть. Для чистоты эксперимента можно вовсе убрать блок try - catch в методе CatchException. Ничего не изменится.
Итак мы убедились что данный код абсолютно бессмысленный. Но в тоже время и вреда от него никакого. Давайте посмотрим на менее безобидные варианты.
Вариант два - "типа обработали исключение"


public static void CatchException()
{
try
{
Wrap();
}
catch(Exception ex)
{
throw ex;
}
}


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


Catch.MyException: Deep exeption
at Catch.Class1.CatchException() in c:\temp\solutions\catch\class1.cs:line 42

at Catch.Class1.Main(String[] args) in c:\temp\solutions\catch\class1.cs:line
12


Что мы видим? Мы видим что MyException возник в методе CatchException, строка 42. Т.е. информация о реальном месте возникновения исключения утеряна. Если вы уходите из пректа и хотите насолить своему приемнику, это вариант специально для вас. Вывод. Если вы хотите после обработки исключения отправить гулять его дальше, используйте голый throw; вместо throw exception;
А вот еще одна часто встречающаяся вариация на эту тему:


public static void CatchException()
{
try
{
Wrap();
}
catch(Exception)
{
throw new ApplicationException("Someting wrong in CatchException");
}
}


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


public static void CatchException()
{
try
{
Wrap();
}
catch(Exception ex)
{
throw new ApplicationException("Someting wrong in CatchException", ex);
}
}


Всего то делов, добавили в создаваемое исключение ссылку на исходное. Результат разительно отличается в лучшую строну.


System.ApplicationException: Someting wrong in CatchException ---> Catch.MyExcep
tion: Deep exeption
at Catch.Class1.RiseException() in c:\temp\solutions\catch\class1.cs:line 24
at Catch.Class1.Wrap() in c:\temp\solutions\catch\class1.cs:line 30
at Catch.Class1.CatchException() in c:\temp\solutions\catch\class1.cs:line 38

--- End of inner exception stack trace ---
at Catch.Class1.CatchException() in c:\temp\solutions\catch\class1.cs:line 42

at Catch.Class1.Main(String[] args) in c:\temp\solutions\catch\class1.cs:line
12


Просто замечательно, видно все. Сначала был MyException в методе RiseException, а потом его обернули в ApplicationException в методе CatchException.
В общем, все это элементарные вещи, может и не стоило писать все это. Но сколько не смотрю кода, кругом одни и те-же грабли разложены. Поэтому и пишу, может кому то поможет.

вторник, апреля 10, 2007

Когда деревья были большими...

В последнее время приходится проводить много профессиональных интервью с программистами. Так вот, я заметил, что многих ставят в тупик вопросы об основных постулатах ООП – наследовании, инкапсуляции, полиморфизме. Складывается впечатление что раньше (несколько лет назад) с этими вещами у программистов были дела получше. Я задумался о причинах такой деградации, и вот какое объяснение пришло мне в голову.
Во всем виновато повсеместное распространение объектных языков и объектного подхода в программировании.
Да – да, именно так. Много лет назад, когда деревья были большими, компьютеры были тоже нехилых размеров. До сих пор не забуду, как на кафедру вычислительной техники доставили новый компьютер... С железнодорожной станции целый день шли Камазы груженные аппаратурой. Нашей группе досталась разгрузка ЦПУ, когда этот, с позволения сказать, процессор освободили от тары и взгромоздили подъемным краном на крыльцо учебного корпуса, мы все почувствовали благоговение перед мощью вычислительной техники. На остатках тары мы прочитали: «Масса брутто 2100 кг. Мaсса нетто 1800 кг.». Перемещали циклопический процессор по коридорам кафедры двадцать человек, опутав его веревками и установив на лист войлока толщиной полтора сантиметра. Подобно древним Египтянам мы воздвигали алтарь нового бога – ЕС 1036. С появлением этой машины, мы могли пользоваться терминалами, раньше приходилось вводить тексты программ и исходные данные на перфокартах.
Впрочем я отвлекся, пустившись в исторический опус, размеры машины тут не причем. В те времена мы изучали программирование на таких языках, как FORTRAN, ALGOL 60, PL1. Только - только появлялись смешные машинки, размером с современный компьютерный стол, с веселым названием ДВК и зашитым в ПЗУ Бейсиком, к которому все мы испытывали искреннее презрение. Тем не менее, все мы твердо усвоили, что программа состоит из двух частей: данных и инструкций по обработке этих данных. Инструкции могут объединяться в подпрограммы процедуры и функции, те с свою очередь могут составлять библиотеки. Объектный подход в корне ломал все. Оказывается можно объединить данные и процедуры по их обработке в одном объекте. Объект это не данные и это не модуль это конгломерат того и другого. Все это необходимо было осознать, пропустить через себя. Области видимости данных, отличные от локальных и глобальных, казались невероятным усовершенствованием. Далее - наследование. Это было что-то фантастическое. Впрочем, наследование, это самая естественная и понятная из парадигм ООП. Полиморфизм - это казалось шаманством чистой воды. Многим было легче понять, как работает его реализация на таблицах виртуальных методов, чем сам смысл этого явления. В общем, для того, что бы научиться объектному подходу, нам приходилось пропускать через себя все его основы.
Тем, кто сегодня начинает программировать, часто так же трудно представить себе, что функция может быть объявлена не в классе, как нам было трудно понять, как к структуре данных можно привязать процедуры и функции. Сейчас любой учебник начинается с описания того, как с помощью визарда создать обработчик нажатия кнопки на форме. И многие долгие годы пребывают в уверенности, что этот обработчик принадлежит классу кнопки а не формы! Умные IDE автоматически подставляют слово override в объявление метода, и немногие могут объяснить для чего оно нужно.
Объектные парадигмы приходят как сами собой разумеющиеся, но мало кто может их с толком использовать. Человек жалуется в форуме: «Я сделал у класса конструктор приватным, а теперь не могу создать его экземпляр, используя имя класса и технологию отражения». Хочется спросить: - «В своем ли ты уме, парень? Ты отрубил голову подозреваемому, и теперь жалуешься на то, что он молчит на допросе». Многие борются с виртуальными методами, пытаясь заставить их работать как не виртуальные...
Может стоит начинать преподавать программирование на каком ни будь АЛГОЛе, PL1 или голом С, что бы потом у людей формировалось истинное понимание сути ООП?

понедельник, апреля 09, 2007

Безопасное подключение к SQL серверу

Практически любое ASP.NET приложение использует БД и в большинстве случаев это SQL Server. Сегодня я хочу поговорить о способах подключения web приложений к SQL серверу. Тема кажется мне интересной, поскольку существует множество нюансов и тонкостей, оказывающих влияния на этот вопрос. Большинство материалов есть в MSDN, но они разбросаны по разным местам, а некоторые аспекты проблемы освещены в документации и вовсе плохо.
Разрабатывая приложение на ASP.NET, которое использует БД мы неизбежно получаем много уровневое приложение, в глубоком бэкэнде которого лежит SQL сервер с базой данных. Он может лежать на одной машине с нашим приложением, а может и на удаленной. С нашим приложением его связывает тонкая нить в виде строки подключения. И от этой строки зависит очень многое. Надеюсь, в вашем приложении эта строка не зашита в коде многочисленных web форм, а располагается в более пристойном месте вроде web.config. Суровая правда жизни состоит в том, что во время разработки в большинстве случаев строка подключения выглядит примерно так:


"User ID=sa;Password=;Initial Catalog=myDatabase;Data Source=(local);"


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


«Data Source=myServerAddress;Initial Catalog=myDataBase;User Id=myUsername;Password=myPassword;»


и так называемый Trusted Connection (я не берусь переводить этот термин, чтобы никого не запутать)


«Data Source=myServerAddress;Initial Catalog=myDataBase;Integrated Security=SSPI;» или другой вариант
«Server=myServerAddress;Database=myDataBase;Trusted_Connection=True;»

При обычном соединении, мы указывает имя пользователя и пароль в строке подключения (именно это обстоятельство и вызывает массу проблем, о чем мы поговорим позже). Тут могут использоваться имена локальных Windows пользователей (локальные учетные записи), имена пользователей домена AD, или иена пользователей SQL сервера, при условии когда на SQL сервере включены оба типа аутентификации: Windows и SQL. В случае Trusted Connection может использоваться только режим Windows аутентификации SQL сервера. При этом имя пользователя и пароль не указываются в строке подключения. Вместо этого данные о пользователе берутся из текущего контекста безопасности приложения. Это обстоятельство также может вызывать массу проблем именно из-за специфики ASP.NET и мы об этом поговорим.
Когда использовать обычное соединение, а когда Trusted Connection? По мере возможности следует отдавать преимущество использованию Trusted Connection. Обычное соединение следует использовать лишь при невозможности установить Trusted Connection, например, если SQL сервер установлен в другой сети, или при хостинге приложения, когда у вас нет доступа к учетным записям Windows, либо когда вы по каким-то причинам вынуждены использовать режим имперсонации пользователя.
Итак, допустим, мы вынуждены использовать обычное соединение и указывать имя пользователя и пароль в строке подключения. Чем может быть вызвано такое решение? Первое, сервер БД расположен в другой сети и в другом домене, который не связан трастовыми отношениями с нашим доменом. Второй случай – это хостинг, когда мы лишены возможности использовать учетные записи Windows. Третий случай, когда по каким либо причинам в нашем приложении включен пользовательский режим имперсонации ASP.NET. Такой режим описывается в файле web.config настройкой следующего вида:


<identity impersonate=”true”>

В этом случае использование Trusted Connection приведет к тому, что каждое соединение с SQL сервером будет устанавливаться от имени пользователя, инициировавшего web запрос. (Подробнее об имперсонации я писал здесь и здесь) Такой вариант имеет право на существование, но используется очень редко. Если вы хотите избежать этого, единственный вариант - указать имя и пароль в строке подключения SQL.
Любой аудит безопасности с радостью ткнет нас мордой в такую строку подключения, и будет в принципе прав. Отчасти поэтому во Framework 2.0 появилась конфигурационная секция . В принципе мы прекрасно хранили строки подключения как ключи в секции , но Connectionstrings предоставляет нам две важные возможности. Первая состоит в том, что теперь мы можем декларативно ограничить использование тех или иных параметров в строках подключения. Вот например как запретить указывать в строке подключения имя пользователя и пароль:


<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=SSPI;"
KeyRestrictions="User Id=;Password=;Persist Security Info=;"
KeyRestrictionBehavior="PreventUsage" />
</connectionStrings>


Вторая возможность, это то, чего нам так не хватало во Framework 1.1 – возможность шифровать строку подключения. Шифровать ее можно именно в секции connectionstrings при помощи утилиты aspnet_regiis.exe:


aspnet_regiis -pe "connectionStrings" -app "/SampleApplication"

После этой операции вы просто не узнаете свой web.config. Впрочем этой же утилитой можно вернуть все в исходное состояние. Подробнее об этой процедуре можно прочитать здесь. У меня есть смутное предчуствие того, что данная фича не будет работать при включенной имперсонации, но к сожалению, не было случая проверить этот кейс на практике.
Теперь рассмотрим Trusted Connection. Что бы понять, под какой же учетной записью наше приложение будет подключаться к SQL серверу в этом случае, надо учитывать несколько факторов. Первый из них, пресловутая имперсонация. Если она включена в режим:


<identity impersonate=”true” />

то при выключенном анонимном доступе подключение пойдет под учетной записью пользователя пославшего web запрос. При анонимном доступе это будет локальная учетная запись IUSR_SERVER_NAME (или та, которая ее замещает).
В режиме имперсонации:


<identity impersonate=”true” username=”Domain\Account” password=”password” />


все подключения к SQL будет от имени Domain\Account (впрочем здесь может быть и локальная учетная запись).
Когда имперсонация выключена, SQL Trusted connection будут происходить от имени учетной записи, под которой исполняется рабочий процесс. Рабочий процесс ASP.net это aspnet_wp.exe для IIS 5.1 и w3wp.exe для IIS 6.0. И тут есть масса вариантов, это могут быть втроенные (Built-in), локальные и доменные учетные записи. Кроме того для разных версий IIS они настраиваются по разному. В любом случае, вы можете узнать под какой записью исполняется рабочий процесс IIS, просто откройте Windows Task Manager и найдите в списке процессов aspnet_wp.exe или w3wp.exe. В столбце “User name” вы увидите имя учетной записи процесса.
Давайте сначала рассмотрим IIS 5.1. Для него настройка переметров рабочего процесса ASP.NET осуществляется в файле mashine.config, секция . Заглянув в эту секцию вы увидите массу интересных вещей, но сейчас нас интересуют только параметры userName и password. Именно они определяют имя учетной записи для рабочего процесса aspnet_wp.exe. Для параметра userName предусмотрено два зарезервированных значения. Значение «Machine» соответствует локальной учетной записи ASPNET и является значением по умолчанию. Значение “System” соответствует одноименной учетной записи. Для этих двух значений в параметре password должно быть “AutoGenerate”. Помимо специальных значений вы можете задать здесь любую локальную или доменную учетную запись. Защитить от не скромных глаз пароль и имя учетной записи можно при помощи утилиты aspnet_setreg.exe как это сделать, подробно описано в MSDN. Совет: если ваши ASP.NET приложения взаимодействуют с COM компонентами, используйте учетную запись System либо доменную учетную запись. Это позволит избежать массы проблем.
Для IIS 6.0 все по другому. Вернее не совсем все :). Если вы откроете MMC консоль IIS, встанете на узел “Web Sites, откроете его свойства на вкладке “Service”, то вы найдете там флажок “Run WWW service in IIS 5.0 isolation mode”. Если отметить этот флажок, то все станет работать и настраиваться, как описано выше для IIS 5.1. В родном режиме IIS 6.0 запускает свой рабочий процесс для каждого Application pool. Соответственно учетная запись настраивается в свойствах пула приложений. В последующем, Application Pool назначается конкретным web приложениям. Изменилась и учетная запись по умолчанию для пула. Теперь это специальная локальная учетная запись NETWORK SERVICE.
Таким образом, мы разобрались с тем, под какой учетной записью будет установлен SQL Trusted connection нашего ASP.NET приложения (кстати проверить так ли это легко, заглянув в Enterprisw Manager в раздел Management – Current activity – Process info). Но для установления соединения нам необходимо завести на SQL сервере соответствующий логин (login), а затем создать в БД пользователя (user) для этого логина и назначить ему нужные роли.
Важно понимать, что когда сервер приложений (IIS) и SQL сервер расположены на разных компьютерах, установить SQL Trusted connection для приложения, работающего под локальной учетной записью не получится. А учетные записи ASPNET, SYSTEM относятся именно к локальным. Т.е. нам надо использовать доменную учетную запись для рабочего процесса приложения. Но настроить пул приложений IIS для работы под доменной учетной записью задача довольно сложная. Здесь описаны проблемы которые могут возникнуть и методы их решения. К счастью, разработчики Microsoft подумали о том как облегчить нашу жизнь и создали для этого специальную учетную запись NETWORK SERVICE. Эта локальная учетная запись обладает замечательной особенностью, ее можно использовать для доступа к ресурсам в локальной сети. При этом доступ к сетевым ресурсам осуществляется не анонимно (как для LOCAL SERVICE), а от имени учетной записи машины (компьютера).
Итак, мы имеем ASP.NET приложение, рабочий процесс которого запущен под учетной записью NETWORK SERVICE и SQL сервер на этой же машине. Для того, чтобы настроить SQL Trusted connection приложения к SQL серверу следует создать на SQL сервере логин NT AUTHORITY\NETWORK SERVICE.
Если же приложение и SQL сервер расположены на разных компьютерах то логин на SQL сервере должен быть NT AUTHORITY\[Computername]$, где [Computername] – это имя компьютера, на котором расположено ASP.NET приложение.

Ссылки по теме:
Working with Connection Strings
How to: Secure Connection Strings When Using Data Source Controls
Code Access Security and ADO.NET
Connectionstrings.com

Paul Graham хоронит Microsoft

Пол Грэм выпустил статью "Microsoft is Dead" (русский перевод).
Ситатья вполне в стиле Web 2.0, начиная с заголовка. По смыслу ей больше подошло бы название "Я больше не боюсь Microsoft", с чем можно поздравить автора.
Ощущение такое, что из мрачного хора гиков, читающих бесконечную мантру "Microsoft must die..." выскочил просветленный Грэм с воплем "Microsoft is dead!".
Хоронят и стебутся над M$ постоянно. Можно вспомнить упреки в адрес Windows 3.1 а потом и Windows 95 за их "вытесняющую многозадачность", и украденный у Маков оконный интерфейc. Можно вспомнить снисходительные коментарии, котрыми сопровождался выход NT на рынок серверных OS. Над SQL server 6.5 не потешался только ленивый. Ну и что? На всех этих рынках сегодня Microsoft чувствует себя вполне комфортно. Microsoft никогда не летела на гребне волны инноваций как Sun или Apple, но она всегда превращает уже созданные инновации в продукты массового спроса, и делает на этом свой бизнес. И пока, у нее это получается лучше чем у других.
Грэм пишет: "All the computer people use Macs or Linux now." Пол, загляни в статистику посещений своего сайта. У меня там вот что:

У тебя, думаю примерно то-же.

понедельник, апреля 02, 2007

Google шутит

Google собирается разводить комаров в России. Читаем "Беспроводной доступ в сеть от Google"
Интересно, сколько резюме от спецов по Mosquito Lisp они получат в результате? :)
Всех с прошедшим первым апреля!